Game 3-5-7 - Game Logic
By the time you reach this tutorial, you should be able to play the game 3-5-7 on a LiveCode stack. The additional functionality covered here shows you how to add a formal two player mode, restrict moves that are not legal and detect which player has won the game.
In order to play 3-5-7 with two players, it is necessary to inform LiveCode when one player is finished and the next player is allowed to start. We can do this by adding a "Next Move" button. This button also serves as an indicator for the player who is executing a move at a particular time in the game.
Add a button as is shown in Game 3-5-7 - A Simple Interface, set the button name to "Next Move", and set the button label to "Player 1", as is shown in the image above. These are the only interface changes required. All further changes are implemented in the code.
The code described in the remaining steps replaces the code that was covered in Game 3-5-7 - Basic Functionality. You should copy and paste the code in the order it appears in this tutorial over the code that you already implemented for the game.
In order to ensure that a player only removes buttons from one line, it is necessary to create a variable that remembers from what line a previous button was removed. We create a global variable for this, allowing the variable content to be visible from all parts of the remaining code.
// Declare a global variable that records from // which line buttons are being removed. local gCurrentPlayLine
If you decide to save the game for future play, we should reset the game when the card opens up. The following command handles openCard events and resets any game state to the game start state. This event handler calls a command that is described later on in this tutorial.
on openCard // Reset the game when the stack opens up. resetGame end openCard
The structure of the mouseUp command in this tutorial is similar to the structure of the mouseUp command in the previous tutorial of this series. We changed the command by added a condition for the new "Next move" button and replaced operational code segments with calls to commands that are implemented in future steps of this tutorial. Moving code into our own defined commands allows us to create a modular and structured flow of the game code.
on mouseUp // Declare two variables. local tButtonName, tControl // Specify a delimiter that allows you to select an item in a line of text. set the itemDelimiter to space // Get the name of the button as was specified it in the Property Inspector. put the short name of target into tButtonName // Test if the mouse click comes from a button or something else. // The first item of the target name specifies the // type of control that sent the mouse message. if item 1 of the name of target is "button" then if tButtonName is "Reset" then // If the reset button was pressed then reset the game. resetGame else if tButtonName is "Next Move" then // If the next move button was pressed // then allow the next player to make their move. nextPlayer else // If a game button was pressed, then remove that button. testAndRemoveButton tButtonName // Test if the current state of the game // is a losing state for the current player. testForWin end if end if end mouseUp
The resetGame command shows all the hidden game buttons and updates the label of the "Next Move" button to indicate that "Player 1" should start the game. The variable gCurrentPlayLine is also set to empty, allowing the player to select the first game button from any line that contains buttons.
command resetGame // Declare a variable. local tControl // Specify a delimiter that allows you to select an item in a line of text. set the itemDelimiter to space // Loop through all the buttons on the card. repeat with tControl = 1 to the number of buttons of me // Make the button visible that we are currently pointing to. set the visible of button tControl to true end repeat // Set the label of the next move button to Player 1. set the label of button "Next Move" to "Player 1" // Ensure that the player can start selecting from any line. put empty into gCurrentPlayLine end resetGame
When a player switches to another player, using the next move button, we need to take two actions:
1. Indicate that the next player can play.
2. Allow the next player to remove the first button of the move from any of the three lines of buttons.
We switch the label of the "Next Move" button by testing the existing value of the label. If the label is "Player 1" then switch the label to "Player 2", otherwise switch the label to "Player 1". The variable gCurrentPlayLine is set to empty in order to allow the next player to remove buttons from any one of the three lines of buttons.
command nextPlayer // If the next move button displays Player 1, then switch it to Player 2. if the label of button "Next Move" is "Player 1" then set the label of button "Next Move" to "Player 2" else set the label of button "Next Move" to "Player 1" end if // Ensure that the player can start selecting from any line. put empty into gCurrentPlayLine end nextPlayer
When it is a player's turn to make a move, the player can remove any number of buttons from only one line of buttons on the board. The initial button selection can be from any line on the board, but subsequent button selections must be from the same line from which the first button was removed. This behavior is implemented by using the gCurrentPlayLine variable. Before a player makes a first move, this variable is empty, once the player has made a move, this variable is assigned the line on which the button was stored. A test in the command described in this step test if gCurrentPlayLine is empty or if a value is set and matches the line number of the button to the value stored in the variable.
The line number is stored in the name of each button and we access this number by stepping through the space delimited items in the button name.
If the player tries to remove a button that is not on the same line as the first button that was removed, then the player receives a message, indicating that the button selection was illegal.
command testAndRemoveButton pButtonName // Specify a delimiter that allows you to select an item in a line of text. set the itemDelimiter to space // Test if the player is allowed to take that button away from the board. if gCurrentPlayLine is empty or item -1 of pButtonName is gCurrentPlayLine then // Make one of the 15 game buttons invisible. set the visible of control pButtonName to false // Put the line number from which the button // was removed into gCurrentPlayLine. put item -1 of pButtonName into gCurrentPlayLine else // If this was an illegal move then inform the player. answer "That is an illegal move. Only take buttons from the same line." with "Okay" end if end testAndRemoveButton
After every move, a test is carried out to identify if the current player is the winner of the game. The test loops through all of the controls of the board and tests for a game button that is visible. If such a button can be found, then the game is not over and the test terminates. If a visible game button cannot be found, then a message is raised, indicating that the current player has won the game. Have a look at the last line of the code to see how this test identifies which player is the winner.
command testForWin // Declare two buttons. local tButtonName, tControl // Specify a delimiter that allows you to select an item in a line of text. set the itemDelimiter to space // Loop through all the buttons on the card. repeat with tControl = 1 to the number of controls of me // Get the name of the button that we are currently pointing at. put the short name of control tControl into tButtonName // If the button we are currently pointing at is visible, then exit this test. if item 1 of tButtonName is "Btn" and the visible of control tButtonName is true then exit testForWin end if end repeat // If we could not find a visible game button, // then inform the user that they have lost. answer the label of button "Next Move" & ", you have won." with "Okay" end testForWin