I'd be the first to admit that I'm a bit of a geek. During the winter holidays, I indulge in my passion for technology, using it as a means to learn and solve puzzles. One of my projects this year was constructing a Tic-Tac-Toe game using SAP Build Apps. This provided an excellent opportunity to delve further into custom components and flow functions. What follows is a tour of the project, not exactly a tutorial, but a pretty good exposure to how I did it. For "pro" SAP Builders, I've included an alternative that involves a bit of JavaScript in SAP Build Apps.
Part of my inspiration for this was the three part series on custom components by @Dan_Wroblewski:
Please see Dan's blogs for more on custom components.
My app is built using a custom component, which I called "GameSpot". To get started, I put a Container on my app canvas, renamed it "GameSpot", and filled it with three Icon components. The icons used were "question", "cicle-o", and "times" from the "Font Awesome" icon library. I also set the font size to 64. In the running game, only one of those Icons is visible at a time. (Three images or three text components could have worked here, as well.)
Then, I clicked on CONVERT TO NEW COMPONENT on the container.
In the context of the new component, I clicked PROPERTIES and added three Private variables: ? Visible, O Visible, and X Visible that control which Icon is shown as the game progresses.
I also added three Custom properites:
At the start of a game, ? Visible is true, so the "?" is visible, and the both O Visible and X Visible are false.
The GameSpot component responds to two events: Component Tap and Property "Show ?" Changed.
On the component tap event, the logic (in the Process Tap flow function) determines if the spot has already been played by checking the ? Visible. False means the spot has already been played, so the function follows the output 2 branch and no updates happens.
Otherwise, the Set X property is used to determine whether "X Visible" or "O Visible" should be set to True. The logic then also updates the value of the Marker property to indicate either "X" or "O" and toggle the value of the the Set X property.
Property "Show ?" Changed
The Show ? property change event is used as a trigger to reset the GameSpot for a new game. It sets the "?" Icon to visible and hides the other two.
I then added nine copies of the GameSpot component laid out in a grid on the app home page and defined two page level properties, Xturn and resetQ. I bound those properties to each GameSpot component's Set X and Show ? properties. (For now, Marker is not bound to anything.)
We now have a functional Tic-Tac-Toe game. But the users have to determine when a "win" happens. (Of course, that's normal for Tic-Tac-Toe.)
In order to detect a winner, the app needs to determine when three Xs ot three Os occur in a line. To do this, we need a data structure that represents the current state of the board and some logic to look at the data structure after each turn to determine if there's a winner.
The current state of the board is represented in the page variable boardList, which is a list of text items. That list starts out as a list of nine "?". After a couple moves is might look like this:
["O", "?", "X", "?", "X", "?", "?", "?", "?"]
In order to keep the boardList updated as play progresses, the Marker property of each GameSpot is set to a formula like this:
pageVars.boardList[1]
The number indicates the index of the "cell" within the grid, with 0, 1, and 2 for the first row, 3, 4, and 5 for the second row, and 6, 7, and 8 for the final row.
Check Three Cells is used to determine if three cells match each other. It takes as inputs, the board list and three cell positions to check, First, it checks whether the first cell is a "?". if so, we know immediately that there's no need to look further. Then, it check whether the first and second cell match and whether the second and thir cell match. On a match, the page variable latestBoardCheck is updated.
The conditions checked are:
NOT(IS_EQUAL(inputs.cellList[inputs.position1], "?"))
IS_EQUAL(inputs.cellList[inputs.position1], inputs.cellList[inputs.position2])
IS_EQUAL(inputs.cellList[inputs.position2], inputs.cellList[inputs.position3])
The latestBoardCheck variable is update used this formula:
{
cellindices: [
inputs.position1,
inputs.position2,
inputs.position3
],
value: inputs.cellList[inputs.position1]
}
We need to look for three in a row for eight different patterns of cells:
The Check Board flow function calls Check Three Cells for each of the patterns.
At the page level, three event triggers have attached logic.
On Page mounted, the page variables are set to their initial state.
On Page variable 'Xturn' changed, the board check, as described above, is executed.
On Page variable 'latestBoardCheck' changed, an alert is presented indicating the win.
The flow function for determining when the game has been one is a little tedious to build using drag and drop editiing. Those with coding skills may find coding that logic a bit easier using JavaScript. Here's what that looks like:
Here's the code:
var board = inputs.boardList;
function detectThreeInARow(cells) {
function checkCells(cell1, cell2, cell3) {
return cell1 !== "?" && cell1 === cell2 && cell2 === cell3;
}
if (checkCells(cells[0], cells[1], cells[2])) {
return { cellindices: [0, 1, 2], value: cells[0] };
}
if (checkCells(cells[3], cells[4], cells[5])) {
return { cellindices: [3, 4, 5], value: cells[3] };
}
if (checkCells(cells[6], cells[7], cells[8])) {
return { cellindices: [6, 7, 8], value: cells[6] };
}
if (checkCells(cells[0], cells[3], cells[6])) {
return { cellindices: [0, 3, 6], value: cells[0] };
}
if (checkCells(cells[1], cells[4], cells[7])) {
return { cellindices: [1, 4, 7], value: cells[1] };
}
if (checkCells(cells[2], cells[5], cells[8])) {
return { cellindices: [2, 5, 8], value: cells[2] };
}
if (checkCells(cells[0], cells[4], cells[8])) {
return { cellindices: [0, 4, 8], value: cells[0] };
}
if (checkCells(cells[2], cells[4], cells[6])) {
return { cellindices: [2, 4, 6], value: cells[2] };
}
return false;
}
var result = detectThreeInARow(board);
return { result: result };
Before this project, most of my SAP Build Apps projects were focused around connections to APIs and usually focused on providing an interface to a some sort of service. Those rarely involved custom components, so this was a great opportunity to learn more about how that works.
Now, what should I do next year? Maybe a Wordle derivative...
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
6 | |
4 | |
2 | |
2 | |
1 | |
1 | |
1 | |
1 | |
1 |