Game development,  Create 1 awesome Othello app with Power Apps 1

Game development, Create 1 awesome Othello app with Power Apps

Game development in Power Apps can be a fun and awesome way of learning how to use the tools.

PowerApps4Kids

Recently I’ve been active with the PowerApps4Kids initiative where quite a few community leaders have shown us their games.

Rory Neary and I organise regular meetups during which children are central. Parents (Big Kids!) and their children learn about Power Apps, and we even had children demo their apps.

In this post I will create a game of Othello aka Reversi using Power Apps.

Do you want to get involved too? Please do have look at the Power Apps 4 Kids website or follow us on twitter.

The rules of the Othello/Reversi game

The game of Othello is also known as Reversi and is played on an 8×8 game board and 64 pieces. The first 4 pieces are placed as shown below and it is blacks turn first to do a place a piece.

Othello Game in Power Apps

You can place a piece only where it is possible to turn one of the other players pieces. In the above screenshot black can place its pieces in the fields marked red.

In the example above black can now turn over a white piece that placed between the piece placed and the black piece that is already there.

This could result in the following game position.

Game development,  Create 1 awesome Othello app with Power Apps 2

Those are the basic rules. For any further details please have a look at the link at the beginning of this post.

As the Othello box used to say, Othello is easy to learn but difficult to master.

Create the game in Power Apps

To create the Reversi or Othello game in Power Apps there are quite a few challenges to overcome. Othello is actually quite heavy on the data complexity.

  • First of all I’m going to have a look at the components model. As I have 64 fields it might make sense to have 64 instances of the Field Component.
  • To do a lot of the calculation/logic work it would help to make use of Collections. in Power Apps.
  • Collections are not fully supported in components
  • A component doesn’t have an OnSelect to run code, or maybe does it? Keep reading this post.
  • Keep the code as simple as possible
  • Reuse code rather than copy code
  • Keep the app performance high, avoid bad code

In this post I will go through these challenges.

Handling whose turn it is

I’m going to create a variable that keeps track of whose turn it is using the following code in the App OnStart code

Set(varCurrentTurn,1);

Then it is black’s turn in the game then varCurrentTurn will be set to 1 and when it is white’s turn I will set this variable to -1.

Why?

To switch turn all I have to do is set the variable my multiplying it with -1. So the following code will do the job.

Set(
        varCurrentTurn,
        -varCurrentTurn
    );

No need for ifs just simply switch it with a single Set.

Data structure to hold the pieces on the board

Within Power Apps we don’t have the easy construction of Arrays. We do however have something that is called Collections. Collections are fairly similar however it is not possible to query a collection to get a single element using somthing like MyArray[3]. We would need to go through multiple functions to get a 3rd item.

This made me create a collection that holds the grid of fields of the game. The following ClearCollect function is run in the App OnStart to create the starting position.

ClearCollect(colPosition, 
{X:1, Y: 1, Value: 0},
            {X:2, Y: 1, Value: 0},
            {X:3, Y: 1, Value: 0},
            {X:4, Y: 1, Value: 0},
            {X:5, Y: 1, Value: 0},
            {X:6, Y: 1, Value: 0},
            {X:7, Y: 1, Value: 0},
            {X:8, Y: 1, Value: 0}, 
{X:1, Y: 2, Value: 0},
            {X:2, Y: 2, Value: 0},
            {X:3, Y: 2, Value: 0},
            {X:4, Y: 2, Value: 0},
            {X:5, Y: 2, Value: 0},
            {X:6, Y: 2, Value: 0},
            {X:7, Y: 2, Value: 0},
            {X:8, Y: 2, Value: 0}, 
{X:1, Y: 3, Value: 0},
            {X:2, Y: 3, Value: 0},
            {X:3, Y: 3, Value: 0},
            {X:4, Y: 3, Value: 0},
            {X:5, Y: 3, Value: 0},
            {X:6, Y: 3, Value: 0},
            {X:7, Y: 3, Value: 0},
            {X:8, Y: 3, Value: 0}, 
{X:1, Y: 4, Value: 0},
            {X:2, Y: 4, Value: 0},
            {X:3, Y: 4, Value: 0},
            {X:4, Y: 4, Value: 1},
            {X:5, Y: 4, Value: -1},
            {X:6, Y: 4, Value: 0},
            {X:7, Y: 4, Value: 0},
            {X:8, Y: 4, Value: 0}, 
{X:1, Y: 5, Value: 0},
            {X:2, Y: 5, Value: 0},
            {X:3, Y: 5, Value: 0},
            {X:4, Y: 5, Value: -1},
            {X:5, Y: 5, Value: 1},
            {X:6, Y: 5, Value: 0},
            {X:7, Y: 5, Value: 0},
            {X:8, Y: 5, Value: 0}, 
{X:1, Y: 6, Value: 0},
            {X:2, Y: 6, Value: 0},
            {X:3, Y: 6, Value: 0},
            {X:4, Y: 6, Value: 0},
            {X:5, Y: 6, Value: 0},
            {X:6, Y: 6, Value: 0},
            {X:7, Y: 6, Value: 0},
            {X:8, Y: 6, Value: 0}, 
{X:1, Y: 7, Value: 0},
            {X:2, Y: 7, Value: 0},
            {X:3, Y: 7, Value: 0},
            {X:4, Y: 7, Value: 0},
            {X:5, Y: 7, Value: 0},
            {X:6, Y: 7, Value: 0},
            {X:7, Y: 7, Value: 0},
            {X:8, Y: 7, Value: 0}, 
{X:1, Y: 8, Value: 0},
            {X:2, Y: 8, Value: 0},
            {X:3, Y: 8, Value: 0},
            {X:4, Y: 8, Value: 0},
            {X:5, Y: 8, Value: 0},
            {X:6, Y: 8, Value: 0},
            {X:7, Y: 8, Value: 0},
            {X:8, Y: 8, Value: 0})
;

Creating the Field component

Creating the Field component is quite a large part of the work. As recently described in my post about custom properties in components we will need to create some properties

I created the following properties.

Game development,  Create 1 awesome Othello app with Power Apps 3

XLocation and YLocation give the component the details of its own location.

The Position property is a table that receives that colPosition variable from the App. CurrentTurn will match the Current turn variable that we created earlier.

Dirty is an output variable that will tell my app that the field was selected by the user.

PossibleMoveOut is set to the following code:

Left.Visible || Right.Visible || Up.Visible || Down.Visible || UpLeft.Visible || UpRight.Visible || DownLeft.Visible || DownRight.Visible

All the other output variables are used to tell the app that a move is possible in a certain direction. These will all be either true or false and are used by the PossibleMoveOut custom property to see if any move is possible. This will then be used to colour the field either red or green.

Is a move possible in a direction

To check if a move is possible in a direction there will be some heavier code.

Game development,  Create 1 awesome Othello app with Power Apps 4

The following code is used for the Visible property of the Left Control. The left Control can be any icon that you might want to use, in my case I went for an arrow that points left. Remember this is low code.

First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation  && Y = cmpField.YLocation
    ).Value
).Value = 0 &&
First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation - 1 && Y = cmpField.YLocation
    ).Value
).Value = -cmpField.currentTurn && (First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation - 1 && Y = cmpField.YLocation
    ).Value
).Value = -cmpField.currentTurn && (First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation - 2 && Y = cmpField.YLocation
    ).Value
).Value = cmpField.currentTurn || (First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation - 2 && Y = cmpField.YLocation
    ).Value
).Value = -cmpField.currentTurn && (First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation - 3 && Y = cmpField.YLocation
    ).Value
).Value = cmpField.currentTurn || (First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation - 3 && Y = cmpField.YLocation
    ).Value
).Value = -cmpField.currentTurn && (First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation - 4 && Y = cmpField.YLocation
    ).Value
).Value = cmpField.currentTurn || (First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation - 4 && Y = cmpField.YLocation
    ).Value
).Value = -cmpField.currentTurn && (First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation - 5 && Y = cmpField.YLocation
    ).Value
).Value = cmpField.currentTurn || ((First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation - 5 && Y = cmpField.YLocation
    ).Value
).Value = -cmpField.currentTurn && ((First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation - 6 && Y = cmpField.YLocation
    ).Value
).Value = cmpField.currentTurn || ((First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation - 6 && Y = cmpField.YLocation
    ).Value
).Value = -cmpField.currentTurn && ((First(
    Filter(
        cmpField.Position,
        X = cmpField.XLocation - 7 && Y = cmpField.YLocation
    ).Value
).Value = cmpField.currentTurn))))))))))))))))

In the above code the app will check if the field that is clicked is empty and if the field next to it is owned by the opponent and if the field after that is owned by the person whose turn it is. The same logic is also built in for multiple pieces of the opponent of course. Hence the long piece of code.

The same as the above code is repeated for each of the directions.

Run OnSelect in Component

Components don’t have a OnSelect property. This is why I added a button to my list of controls. This button is made transparent by giving it the following colour for its fill property:

RGBA(0, 0, 0, 0)

Now all I had to do was to bring it to the front of the component, so that is appear in front of anything else in the control.

When the button is clicked the dirty property is set and the app can pick up the changes as we need them.

Set(
    varDirty,
    true
)

Why using this dirty property? Can a component not just run more code? It would indeed be possible if components were happy to use collections. But collections aren’t fully supported yet.

So far I found that you can pass in a collections but passing out causes some troubles. But the behaviour doesn’t seem to be consistent.

Creating the Othello/Reversi fields

Time to create 64 fields. This is a bit painful, but I created 64 fields and named the to include their coordinates in the grid.

Othello/Reversi game in Power Apps

To help myself a little bit I grouped the fields in rows, but this of course doesn’t matter too much. Then for each field I set the input properties.

Game development,  Create 1 awesome Othello app with Power Apps 5

Resetting the fields

When A move has been done, we will need to tell the component to reset.

When the component is reset the Dirty variable that is used to set the Dirty output property is set to false.

Set(
    varDirty,
    false
);

Setting this variable to false will make sure that the move timer as described below will not run anymore until another field has been selected by a user.

Playing the Othello/Reversi moves

For playing the moves we have two problems to solve.

  1. Identify that a component has been clicked
  2. Turn the right pieces.

I created a time control on my screen that has the start property set to

Field1_1.Dirty || Field1_2.Dirty|| Field1_3.Dirty|| ... || Field8_8.Dirty

And I’ve set the timer’s duration to 1 second.

Then the timer will run the following code. In the first part of the code we check if any of the fields is dirty. Then a collection is created to contain each of the directions, before a collection colToBeMoved is created.

If (
    !IsEmpty(
        Filter(
            colAllFields,
            Field.Dirty
        )
    ),
    Set(
        varDirtyField,
        First(
            Filter(
                colAllFields,
                Field.Dirty
            )
        ).Field
    );
    
    
// Update the next fields that need turning
ClearCollect(
        colDirections,
        Table(
            {
                X: 1,
                Y: 1,
                Possible: varDirtyField.DownRightPossible
            },
            {
                X: -1,
                Y: 1,
                Possible: varDirtyField.DownLeftPossible
            },
            {
                X: 1,
                Y: -1,
                Possible: varDirtyField.UpRightPossible
            },
            {
                X: -1,
                Y: -1,
                Possible: varDirtyField.UpLeftPossible
            },
            {
                X: 1,
                Y: 0,
                Possible: varDirtyField.RightPossible
            },
            {
                X: -1,
                Y: 0,
                Possible: varDirtyField.LeftPossible
            },
            {
                X: 0,
                Y: -1,
                Possible: varDirtyField.UpPossible
            },
            {
                X: 0,
                Y: 1,
                Possible: varDirtyField.DownPossible
            }
        )
    );

    ClearCollect(colToBeMoved, {
            X: varDirtyField.XLocation,
            Y: varDirtyField.YLocation,
            Value: varCurrentTurn
        });

    ForAll(
        colDirections,
        ForAll(
            colDistance,
            ForAll(colPosition,
              If (Value = -varCurrentTurn &&
                Possible && 
                X = varDirtyField.XLocation + colDirections[@X] * colDistance[@Distance] && 
                Y = varDirtyField.YLocation + colDirections[@Y] * colDistance[@Distance],
               Collect(colToBeMoved, 
                {
                    X: varDirtyField.XLocation + colDirections[@X] * colDistance[@Distance],
                    Y: varDirtyField.YLocation + colDirections[@Y] * colDistance[@Distance],
                    Value: varCurrentTurn
                })
              )
            )
        )
    );

// Had to collect tobe moved before doning updates as possible moves will otherwise be affected.
ForAll( colToBeMoved,  
    UpdateIf(
        colPosition,
        X = colToBeMoved[@X] && Y = colToBeMoved[@Y],
        {
            X: colToBeMoved[@X],
            Y: colToBeMoved[@Y],
            Value: varCurrentTurn
        }
    );
);


    Set(
        varCurrentTurn,
        -varCurrentTurn
    );
    Reset(varDirtyField);
    
);

Once the colToBeMoved has been created the right fields are turned over. Noticed that this has to be done in two steps. If we started to turn the pieces while identifying the pieced that needed to be turned then the components for the fields would start to respond immediately and pieces will not be changed.

Next steps

Please do create this Othello or Reversi app to see what you can do to it and how you can improve it.

Can you get the same result as I did? Or can you make it better?

Do you have any ideas that you want to create an app for ( game or not) but you don’t know where to get started. Please feel free to contact me using any of the contact options available on this site.

Did you make any other games yourself? Please do leave a comment below, or some and present your game at one of the next PowerApp4Kids sessions.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: