In this series of posts we’ll run through how to create the game of Pontoon running in the cloud on the GameSparks platform.
This post will detail the process we followed and the steps required to get the core logic working, the next post will show how we test the logic using the test harness.
1. Create a new game
We’ll start by creating a new Game in the portal. We’ll call it Pontoon, and use the empty game template so we can start from scratch.
2. Create Events
There are 2 player events that happen in pontoon that we care about, Twist and Stick. We’ll create an event for each. The events do not require any data to be passed, so no attributes need to be created. We’ll use TWIST and STICK as the respective short codes for the events.
3. Create a Challenge
We need to define how a challenge should behave. Pontoon is a turn based game, where each player takes a number of goes before either sticking or going bust. We’ll create a new Challenge that defines itself as turn based and uses the Stick event to move to the next player. The process of going bust will need to be calculated in our cloud code. There is no leaderboard so we’ll specify the outcome is scripted.
4. Challenge Setup
When the challenge starts, we need to define a deck of cards and shuffle them, this deck can then be stored against the challenge with the shuffled order maintained for each player as they take their turns. To do this we create some cloud code that binds to the ChallengeStartedMessage. This message is generated by the platform when the challenge begins and runs once per challenge.
The following functions were created in a new module that would allow us to include it into any of our scripts (Credit to BrainJar for saving us from having to write this from scratch)
We create a cloud code module with the sort code of “cards” to store this code.
The complete listing for the cards module:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | //This function creates and array of cards, and populates it with 'numberOfDecks" worth of decks. //This is not used externally but is extracted into it's own function for readability function makeStackOfDecks(numberOfDecks) { var ranks = new Array("A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"); var suits = new Array("C", "D", "H", "S"); var i, j, k; var m; m = ranks.length * suits.length; // Set array of cards. deck = new Array(numberOfDecks * m); // Fill the array with 'n' packs of cards. for (i = 0; i < numberOfDecks; i++){ for (j = 0; j < suits.length; j++){ for (k = 0; k < ranks.length; k++){ deck[i * m + j * ranks.length + k] = new Card(ranks[k], suits[j]); } } } return deck; } //This function calls makeStackOfDecks and uses the Fisher–Yates method to shuffle the deck function makeShuffledStackOfDecks(numberOfDecks) { var stack = makeStackOfDecks(numberOfDecks) var i, j, k; var temp; // Shuffle the stack 'n' times. for (i = 0; i < 10; i++){ for (j = 0; j < stack.length; j++) { k = Math.floor(Math.random() * stack.length); temp = stack[j]; stack[j] = stack[k]; stack[k] = temp; } } return stack; } // An object representing a card that we can save in the challenge. function Card(rank, suit) { this.rank = rank; this.suit = suit; } |
Once the module is done, we can create a Script and bind it to the ChallengeStartedMessage. It includes the module, creates a stack of cards and creates an empty had for each user. This data is stored against the challenge for further script invocations.
We created a Global Message Script and bound it to the ChallengeStartedMessage event:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //Loads the cards module with the functions we need gs_load("cards"); //Gets the current challenge so we can populate it with cards and hands //This uses the GameSparks "Spark" API (Documented in the portal) var challenge = Spark.getChallenge(Spark.data.challenge.challengeId); //Use the module method to create a stack of cards using 2 decks //We'll store this in private data to no-one can ever work out //what card comes next. challenge.setPrivateData("deck", makeShuffledStackOfDecks(2)); //Create an empty array to hold the hand state var hands = []; //Gets the list of player ID's who have accepted the challenge var accepted = challenge.getAcceptedPlayerIds(); //Create an empty hand object for each player and add it to the hands array for(i=0 ; i<accepted.length ; i++){ hands[i] = {id:accepted[i], hand:[]}; } //Store the initialised hands array against the challenge challenge.setScriptData("hands", hands); |
5. Taking a turn
When the challenge starts, the platform assigns a random player to be the first. The first player will ask for cards to be twisted until they either stick or go bust. The first thing we need to do is handle the twist event.
We’ll create a module that contains common code for taking a turn, this can be used by both the stick and twist events. We save this with a short code of “turns”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | //Global var to access the challenge we are dealing with var challenge = Spark.getChallenge(Spark.data.challengeInstanceId); //Global var to access the current deck var deck = challenge.getPrivateData("deck"); //Global var to access the current hands var hands = challenge.getScriptData("hands"); //Global var to access the current players hand var currentPlayersHand = getCurrentPlayersHand(hands); //Gets the current players hand out of the hands object function getCurrentPlayersHand(hands){ for(i=0 ; i<hands.length ; i++){ if(hands[i].id == Spark.player.playerId){ return hands[i]; } } } //Method to determine whether the game is finished. //we track a "complete" attribute against finished hands function checkFinished(hands){ var isFinished = true; for(i=0 ; i<hands.length ; i++){ if(hands[i].complete){ isFinished = isFinished && hands[i].complete; } else { isFinished = false; } } if(isFinished){ calculateWinner(); } } //Works out who has won by sorting by the hand score function calculateWinner(){ hands.sort(function(a,b){return a.score-b.score}); var winner; for(i=0 ; i<hands.length ; i++){ if(hands[i].score <= 21){ winner = hands[i].id; break; } } if(winner){ challenge.winChallenge(winner); } else { challenge.drawChallenge(); } } //Calculates a score for a hand function handGetScore(hand) { var i, total; total = 0; // Total card values counting Aces as one. for (i = 0; i < hand.length; i++) if (hand[i].rank == "A") total++; else { if (hand[i].rank == "J" || hand[i].rank == "Q" || hand[i].rank == "K") total += 10; else total += parseInt(hand[i].rank, 10); } // Change as many ace values to 11 as possible. for (i = 0; i < hand.length; i++) if (hand[i].rank == "A" && total <= 11) total += 10; return total; } |
Now we have the turns module we can use it in a Challenge Event Script bound to the TWIST event:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //Load the turns module gs_load("turns"); //Add the card from the top of the deck to the current players hand currentPlayersHand.hand[currentPlayersHand.hand.length] = deck[0]; //Now remove that card from the deck, and save the deck deck.splice(0, 1); challenge.setPrivateData("deck", deck); //Calculate the hand score currentPlayersHand.score = handGetScore(currentPlayersHand.hand); //Is the current player bust? if(currentPlayersHand.score > 21){ //They are, so move the current player to the next player. challenge.consumeTurn(Spark.player.playerId); currentPlayersHand.complete = true; } //Save the hands back to the challenge challenge.setScriptData("hands", hands); //Run the check to see if the game is complete checkFinished(hands); |
Next we create it in a Challenge Event Script bound to the STICK event:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //Load the turns module gs_load("turns"); //All we need to do here is validate the user has at least 17 if(currentPlayersHand.score<17){ Spark.setScriptError("msg", "You only have " + currentPlayersHand.score + ", you need 17 or more to stick"); } else { //Mark the players hand as complete currentPlayersHand.complete = true; //Then save the hands back into the challenge challenge.setScriptData("hands", hands); //Now check if the game is finished checkFinished(hands); } |
And with that, all the code is complete. The game is pretty simple so we’ll add further capabilities in future posts.