Hi there i’m a CS undergraduate and would like to help some of you by sharing a bit of my knowledge.
As of now this guide is incomplete many additions will be made through next the weeks. The purpouse of this guide is to analize and give not only a more analitical perspective but also share some tips and tricks.
From the start I will metion Computational Cost ( O(1),O(n),…O(n!) ) that will be explained later in the computational cost section don’t worry if you don’t understand at first.
Data Types
strongly typed means you do not have to specify the data type that will be held in a variable
ARRAYS
An Array is a type of data structure used to hold multiple data in an orderly fashion. It can either be homogeneous or heterogeneous and is usually represented with the square brackets:
array = [2,3,46]
BEWARE
*In OW Workshop you cannot put strings into arrays only float/double, int and objects. (for objects see OOP online for more information)
BEING PRECISE
OW as some other programming languages doesn’t have the Array Type but rather something called a List or an ArrayList.
*The difference lies in the fact that with arrays you have to specify the length when creating it and once you have occupied all slots you must create another one an add one at a time all the elements of the previous array to sort of add more slots to it.
List do this automatically unless they are linked list, in that case they are kind of different.
HOMOGENOUS
It means that it can only hold one data type at a time. Most programming languages like C,C++ or Java only support homogeneous arrays and in that case it must be first initialized, specifying the type it will hold:
JAVA EXAMPLE
/*just a java implementation it is not well commented so don't bother with it unless you are curious enough to do research.
*/
List<Integer> list = new ArrayList<>();
//list.add("alfa") doesn't work: throws an error
list.add(3) //works
for (Integer i : list) System.out.println(i); // this will print every element of list
// list = [3]
HETEROGENEOUS(USED BY OW)
It means it can hold all data types at the same time unlike homogeneous arrays/list that can hold only one data type at a time.
Programming languages like Javascript or Python use this kind of arrays/list because they are not strongly typed languages. The same applies to OW Workshop which is not strongly typed.
**However it is deemed good practice to make homogeneous arrays/list even though you can make them heterogeneous.
PYTHON EXAMPLE
array = [] #initializes array as a list
array.append(3) #adds 3 to the array
array.append("alfa") # adds the string "alfa" to the array
print(array) #will print [3,"alfa]
OW ARRAY METHODS
CREATING AND ADDING ELEMENTS
To create an array in OW Workshop you have to set a variable (be it a global or player variable) as an empty array.
Set Global Variable(A,Empty Array)
This will initialized: A = []
*Now you can start adding elements to the array using the append method.
Modify Global Variable(A,Append To Array,0)
This will add 0 to A: A = [0]
**Both creating and adding should cost O(1) ammortized.
INDEXING
Since arrays are ordered depending on the setting order, you can access each position using a number.
This numbers range from 0 which can access the first position of the array to the length of the array minus 1 which will access the last element of the array.
For Example:
#PYTHON CODE
a = [2,4,10,1039]
print(a[0]) #will print 2
#in PYTHON square brackets are used to access elements in lists,tuple and dicts.
**We can also access a position and overwrite its values like this:
#PYTHON CODE
a = [2,4,10,1039]
a[3] = 10
print(a[3]) #will print 10 instead of 1039
*In OW Workshop you can use the value in array and modify global/player variable at index to do this:
Set Global Variable(B, Value In Array(Global Variable(A), 0));
**This would set B as the first value in the array A.
Alternatively in OW you can also use:
Set Global Variable(B,First Of(A))
Set Global Variable(B,Last Of(A))
//instead of using:
Set Global Variable(B,Value In Array(A,0)
Set Global Variable(B,Value In Array(A,Subtract(Count Of(B),1))
**Whereas to Modify a value at a certain index you will just need to use:
Modify Global Variable At Index(A, 0, Add, 2);
**Both operations (Getting and Setting a Value in an Array, ) have cost O(1).
Meanwhile Index Of Array Value and Remove From Array By Value/Index are both O(n) operations.
INDEX OF
- Index Of or Index Of Array Value is a method that searches each element of the array keeping the current index saved and returns it after finding said element.
Whereas removing from an Array will not search the value if the Index is given however it will still need to re-organize the array after eliminating the element.
For Example
# 0,1,2,3 indexes or positions
a = [2,3,4,6] #array
# if we where to do
a.remove(1) #removes the second element of the array
#in Python the pop method is used to remove elements given a position
#while the remove is used to remove elements given a value so the instruction above is incorrect.
# the array would become: a = [2,4,6]
#to do this there are various methods such as cycling the array from the index position to the end of the array and setting the next value to the current index.
# 0,1,2,3 positions or indexes
#a = [2,missing element,4,6]
#a = [2,4,4,6]
#a = [2,4,6,6]
#a = [2,4,6,null] last position gets set to null and excluded from the array
#a = [2,4,6] remaining array seen with the print
The method Remove From Array By Value will be done starting from the first location like this:
a = ["alfa","beta","gamma","delta"]
a.remove("gamma") #we remove the third element of the array: "gamma"
# we start at pos = 0 and a[0] = "alfa" and "alfa" != "gamma"
#then pos+=1 so pos = 1 and a[1] = "beta" -> a[1] != "gamma"
#then pos+=1 so pos = 2 and a[2] = "gamma" -> a[2] == "gamma"
#now having found the element we were looking for, since it looks for the first element
#we remove "gamma so a = ["alfa","beta",null,"delta"]
#now we continue and reorganize the array so a = ["alfa","beta","delta",null]
#after cycling all the array it stops.
For and While Loops
BEWARE
For Loops and While Loops are inexistent in OW Workshop
However you can still implement them in a way less efficient way.
Definition
A loop let’s you repeat an action until a condition is met.
FOR LOOPS
A For loop usually initialises a variable and apply a function to it until the condition is met.
Example of For loop in Java
//the loop initialises a variable **i** to **0**
for(int i=0;i<10;i++) {
//at every cycle tests the condition, in this case i<10 and if true stops otherwise applies the function, in this case i++ which increments i by one value.
System.out.print(i+" "); //so in this case It will print 10 times the value of the variable i and a space
//so: 0 1 2 3 4 5 6 7 8 9
}
A BADLY DESIGNED FOR LOOP
I have never done this but theoretically you could make this kind of for loop even though it would be totally incorrect. So please don’t do it, it’s just to reflect on the nature of loops.
//loop with unclearable conditions
for (int i=0;i!="a";i++) {
System.out.print(3);
}
//this would forever print the number 3
//because no matter how much you increment it, **i** will never become equal to the string "a"
Usually for loops are used to cycle numerical values.
For Example
//Java Version
for(int i=0;i<20;i+=2) {
System.out.println(i);
}
//in this case we increment **i** by 2 on every cycle
#Python Version
for i in range(20):
print(i)
#does the same stuff but every cycle increments **i** by 1
Cycling an Array with For Loops
*Let’s say you want to check every element of a numeric array and see if it’s equal to 2
To do this we can use the length (Count Of in Ow) method.
//Java Version
int[] ourArray = new int[] {10,3,8,2,7,6};
//we initialise our typed homogeneous array as a numeric one as [10,3,8,2,7,6]
for (int i=0;i<ourArray.length;i++) {
if(ourArray[i] == 2) {
System.out.println("index of 2: "+i);
break; //stops the cycle once it finds the value we were looking for
}
}
//let’s represent each cycle as **Tc** where **c** is the number of the cycle
// at T0 we have i=0 and ourArray[i] = 10 (first element of the array at position 0)
//with **i**=0 ourArray[i] != 2 (10 != 2)
//at T1 we have i=1 and ourArray[i]= 3
//we keep going until T3
//at T3 we have i=3 and ourArray[i]=2
//so we enter the **if** statement, print **i** and using the break end the loop since we have found what we needed.
#Python Version
ourArray = [10,3,8,2,7,6]
#in python we can cycle in two ways using a for loop:
#first way like in java
for i in range(len(ourArray)): # len is the equivalent of Count of or length in Python.
#Range is a special data type that represents numbers that range from the start to the end(not included). Range(start,stop,step)
#It can be visualized as an array and in this case is like cycling a numeric array [0,1,...len(ourArray)-1]
#range(0,2) could be visualized as [0,1]
If ourArray[i]==2:
print("index of 2:",i)
break
#Or we can cycle like this. Even though it wouldn't work for what we are trying to do right now.
for i in ourArray:
if i == 2:
print("2 is in the array")
#this implicitly creates and increments **i** by one until it is equal to the length of our array.
#However at each cycle instead of having **i** we have the element in the **Ith** (i) position of our array (ourArray)
WHILE LOOP
A while loop is a loop that continues as long as a certain condition is cleared/met.
We can recreate a for loop using a while loop by simply initializing a variable and applying a function to it on every cycle.
#JAVA VERSION
public static void main(String[] args) {
int i = 10;
while(i > 0) { //the while loop **checks** if the condition is **true** on every cycle, if not it stops.
System.out.println(i); //we print i every cycle
i--; //we decrement **i** on every cycle
}
//this will print: (We still keep in mind(Tc) where T represents the time and c as the number of cycle
//T0 10
//T1 9
//....
//T8 2
//T9 1
//which is basically like doing:
for(int i=10;i > 0;i--) { //same output
System.out.println(i);
}
}
We can set whatever option for the while loop but we still have to keep in mind that the condition must be clearable or else we are stuck in an infinite loop.
//JAVA VERSION FOR OTHER TYPE OF LOOP
String[] ls = new String[] {"alfa","beta",""};
int pointer = 0;
int lastIndex = ls.length-1;
String[] holder = new String[] {"g","a","m","m","a"};
while(ls[lastIndex] != "gamma") {
ls[lastIndex] = ls[lastIndex]+holder[pointer]; //on every cycle we modify the last position of our array ls by adding the current element of the array **holder**
pointer++; //we increment the pointer so the current element of **holder** changes.
//T0:
// ls = ["alfa","beta",""], holder = ["g","a","m","m","a"] and pointer = 0
// since ls[2] != "gamma" the cycle starts
//holder[pointer] = "g"
// we modify ls[2] and we increment pointer
//T1:
//ls = ["alfa","beta","g"], holder is the same and pointer = 1
//since ls[2] (now "g") != "gamma" the cycle continues
//holder[pointer] = "a"
//we modify ls[2] and increment pointer
//T2:
//ls = ["alfa","beta","ga"], pointer = 2
// ls[2] = "ga" and "ga" != "gamma"
//T3: ls = ["alfa","beta","gam"], pointer = 3
//T4: ls = ["alfa","beta","gamm"], pointer = 4
//T5:
//ls = ["alfa","beta","gamma"], pointer = 5
//so since ls[2] == "gamma" the condition is now false and the cycle stops
//if it stops it doesn't run the code.
//if it would we would go outOfRange because pointer == holder.length
}
//JAVA EXAMPLE OF AN INFINITE CYCLE
//let's say we take the last code and modify it by eliminating the array holder and the variable pointer
//and let's say that we only want one cycle so we make it like that:
while(ls[ls.length-1] != "gamma") {
ls[ls.length-1] = ls[ls.length-1]+"gamma";
}
//naturally this code doesn't work and is so unrealistic it bothers me.
//in this case if we have:
ls = ["alfa","gamma","beta"]
//then it will cycle forever. Unless the last element of **ls** is equal to a blank string that it will be an infinite loop
//more realistic infinite loops
//1-
while(true) {
//do something
}
//2-
//where f is a global variable
//Game represents OW and F is our global variable which in JAVA should be static
while(Game.F ) {
//do something
}
//the first one is intentional and sometimes used in some types of game loops
//the second one can be done by a mistake
//let's say that F contains a boolean
//for a semantical mistake, we have written F instead of V which gets changed by the code of one of our rules/functions and F always holds a **true** value
RECURSIVE LOOP
A recursive loop is a loop made using a function that calls itself until a certain condition is cleared.
For Example
#PYTHON VERSION
def foo(counter,array):
if counter >= len(array):
return
print(array[counter])
return foo(counter+1,array)
foo(0,["alfa","beta","gamma","delta"])
#we start by calling our function **foo**
ASSEMBLY AND OW LOOP
Pile and Stack
The Pile and the Stack are powerful tools if used correctly.
Basically a pile is a FIFO(First In First Out) which means that it is an array where the first element added will also be the first element to be removed.
Meanwhile a stack is LIFO(Last In First Out) which means last element added will be the first to be removed.
Now a +stack* or a pile assumes that you have to do various operations to elements that you store inside and when you don’t need them anymore you remove them.
Methods
*In Programming languages such as Java, stack and pile have generally two methods: append or add and pull if it’s a stack it will be pull last otherwise it’s pull first
*They are usually implemented with Linked Lists.
Example of a Pile in OW
Timed Pile
Let’s say I wanted to create effects that last a few seconds and afterwards I want to destroy them.
I could store them in a variable then lets say wait(5) and use destroy Effect(variable) however this doesn’t work if you have to do it multiple times because you would need them to store in a player variable array wait(5) per each and it would become a mess with all those player arrays.
So let’s say we make it global and store all of them in a global array like this:
variables
{
global:
0: objectPile
}
rule("effect Pile")
{
event
{
Ongoing - Global;
}
conditions
{
Global Variable(objectPile) != Empty Array;
}
actions
{
Wait(5, Ignore Condition);
Destroy Effect(First Of(Global Variable(objectPile)));
Modify Global Variable(objectPile, Remove From Array By Index, 0);
Loop If Condition Is True;
}
}
As long as the pile is full elements will be removed from it every 5 seconds.
Now this still has a problem or two…
Let’s say I want an effect only to last 5 seconds, if we append it to the global array and it is at the third position then it will have to wait 15 seconds before being destroyed.
This can be solved using match time to your advantage:
variables
{
global:
0: EffectPile
1: TimerPile
}
rule("EFFECT PILE")
{
event
{
Ongoing - Global;
}
conditions
{
Global Variable(EffectPile) != Empty Array;
}
actions
{
Wait(0.500, Ignore Condition);
Skip If(Compare(Match Time, >, First Of(Global Variable(TimerPile))), 3);
Destroy Effect(First Of(Global Variable(EffectPile)));
Modify Global Variable(EffectPile, Remove From Array By Index, 0);
Modify Global Variable(TimerPile, Remove From Array By Index, 0);
Loop If Condition Is True;
}
}
Now if we have an effect that we want to last 5 seconds we just have to create the effect, append the last created entity to the EffectPile and append to the TimerPile the current match time minus 5.
However this still has some issues:
*First of if you create an effect during the setup phase it will never be removed unless you are 5 or 10 seconds from the end of the match.
*Another problem is efficiency wise, since every time we remove an object we are doing a O(n) operation unless we are using a Linked List (in that case is O(1) )
To solve the first problem we could either:
-forbid creation of effects during the setup phase
-every cycle sort the Pile so sort the TimerPile and with it find a way to sort the EffectPile accordingly to the position of the TimerPile
*To solve the second problem you can use a variable to store the current index and increment it every time you would remove the first element from the EffectPile and TimerPile.
If you don’t like having astronomically huge list/arrays then you could set a constant,either in another global variable or store it in a skip if function.
rule("effect Pile") {
variables {
global:
0: effectPile
1: TimerPile
2: index
}
event {
Ongoing-Global
}
conditions {
Global Variable(effectPile) != Empty Array
}
actions {
Wait(0.500, Ignore Condition);
Skip If(Compare(Match Time, >, Value In Array(Global Variable(TimerPile), Global Variable(index))), 5);
Destroy Effect(Value In Array(Global Variable(effectPile), Global Variable(index)));
Modify Global Variable(index, Add, 1);
Skip If(Compare(Global Variable(index), <, 100), 2);
Set Global Variable(effectPile, Array Slice(Global Variable(effectPile), Global Variable(index), Subtract(Count Of(Global Variable(
effectPile)), Global Variable(index))));
Set Global Variable(effectPile, Array Slice(Global Variable(TimerPile), Global Variable(index), Subtract(Count Of(Global Variable(
TimerPile)), Global Variable(index))));
Loop If Condition Is True;
}
}
Elimination Pile
*Let’s say you don’t need a Pile for timed effects like smoke bombs effects or sandstorms or maybe tagging someone or a location with an icon for a certain time.
*So what about setting a destructible effect:
**we assume that we already have:
-An effectArray that contains the IDs of various effects (no icons or hud text)
-An array called hpArray that at the Nth position contains the health value of the Nth effect.
-A function that when shooting gets the index of the effect in front of the event player
**Now to eliminate an element we can either use a pile or put the code inside the shoot function/rule.
•Using An Elimination Pile
In this case we will have a rule that sees if the eliminationPile is not empty and will wait the minimum amount of time to loop.
If not empty it will destroy the effect and modify the two arrays.
*To implement this we make an empty array aka our pile that will contain the indexes of our effectArray. (The destructible effect array)
Entity Type
We can also decide between which type of entity(effect/icon/hud text) by making another array that will hold a number that defines which type of entity it is store at the Nth location.
#let’s say each **entity** is represented by a numeric code made by 6 numbers.
#let’s say each entity type is encoded like this:
#0 = effect
#1 = icon
#2 = hud text
effectArray=[109733,284631,197395]
healthArray= [100,10,90]
typeArray=[0,1,2]
*Now assuming you have a rule that gets the index of the element to eliminate and appends the index to our elimination pile if the hp of the entity is less than a certain amount (in this case 0).
*The elimination pile works like a timer pile waiting the smallest amount of tine possible and it doesn’t the skip condition for the match time.
**If you have multiple arrays setup like our effect array above, then you could do two things:
Either create another pile that works in conjunction with our elimination pile, encoding each global array as a number (meaning with an ID)
Or fuse all arrays into a single one and create a support array like healthArray is for our effectsArray that contains for each element a number which specifies which element can access it.
This check would be done in your rules. However it is more messy and on a database stand point way an erroneous approach.
*When to use an elimination pile:
-If you need to eliminate a lot of effects or entities and want to make te code cleaner
-if you have big arrays and don’t want your rule/thread to get stuck removing values or destroying entities.