Building OpenSocial Apps- P4
lượt xem 8
download
Building OpenSocial Apps- P4: Nhà phát triển của Thư viện Series từ Addison-Wesley cung cấp hành nghề lập trình với độc đáo, tài liệu tham khảo chất lượng cao hướng dẫn về các ngôn ngữ lập trình công nghệ mới nhất và họ sử dụng trong công việc hàng ngày của họ. Tất cả các sách trong thư viện của Nhà phát triển được viết bởi chuyên gia công nghệ các học viên những người có kỹ năng đặc biệt tại các tổ chức và trình bày thông tin một cách đó là hữu ích cho các lập trình viên khác....
Bình luận(0) Đăng nhập để gửi bình luận!
Nội dung Text: Building OpenSocial Apps- P4
- 124 Chapter 7 Flushing and Fleshing: Expanding Your App and Person-to-Person Game Play only the picked opponent in the availableGames div. Later on we’ll make it do something more interesting. function opponentPickedAction(person){ var elem = document.getElementById("availableGames"); elem.innerHTML = person.getDisplayName(); } 4. Immediately after the opponentPicked function, add the following loadFriendPicker function.This function makes use of the widget Bootstrapper object to initialize the FriendPicker widget.The Bootstrapper allows us to safely create a widget from a dynamically included script library.The call to createWidget takes three parameters: the object name of the widget being created, an initializer function to fire when the widget has been created, and an object of parameters to pass into the widget at initialization. In this case, we’re passing in the ID of our target div element, a callback function to execute whenever a friend is picked, and a flag to build the “Selected User” user interface element. function loadFriendPicker(){ MyOpenSpace.Widgets.Bootstrapper.createWidget( "MyOpenSpace.Widgets.FriendPicker", function(p){ window.friendPicker = p; }, { element: "opponentPicker", buildSelectedUI: true, friendClickAction: opponentPicked }); } 5. In our loadInitializer function, add a call to loadFriendPicker: function loadInitializer(){ try{ loadWordOfTheDay(); } catch(ex){} try{ initializeGame(); } catch(ex){} loadFriendPicker(); } Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- Turn-Based Games 125 Figure 7.4 FriendPicker below player info. Warning The call to initialize a FriendPicker must be made from a gadgets.util. registerOnLoadHandler-triggered function. If we try to initialize from an inline call, the initializer will fail in some browsers. Not all browsers handle dynamic script loading in the same manner, so we must use the least common denominator behavior. If we save and view our app, we’ll see the FriendPicker initialized below the current viewer UI. Clicking on the [Click for Recipient] item shown in Figure 7.4 brings up the FriendPicker UI and allows the user to pick a friend. App Data Game Store Our game store in app data will consist of some JSON objects in an array.We’ll build on some of the code shown previously in the book for using the app data store.This time we’ll clean things up a bit by placing all our code under the TTT namespace, instead of leaving a bunch of dangling functions hanging off the global window object. Storage JSON Objects The first step is to create the GameInfo storage object. In your script code, add the following code: TTT.GameInfo = function(opponentId){ this.opponentId = opponentId; this.dataSourceId = window.viewer.getId(); this.boardLayout = null; this.moveNumber = 0; this.currentPlayer = 0; this.winner=0; } With this object, our game can track the opponent, whose move it is, if there is a win- ner, and all the moves on the board.The active games are stored as an array of these objects. AppDataPlay Game Object The next step is to set up our basic game data manager.This will contain the logic described earlier in the chapter. It will also encapsulate some utility methods for managing the data store. Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- 126 Chapter 7 Flushing and Fleshing: Expanding Your App and Person-to-Person Game Play Logging to Debug Apps The gadgets spec provides for a logging mechanism similar to the one popularized by log4j under the gadgets.log call. Logging is an incredibly useful way to debug an app. It’s not always convenient to set breakpoints and single steps. A log lets you dissect the execution sequence after the fact. At the time of this writing, gadgets.log isn’t implemented in the MySpace container. It’s simple to add, though. In our case, we just want to write out to a scrolling div. After development we can hide the div and leave the log calls in place. Here’s how: #log{ float:left; margin:5px; padding:5px; border:5px solid #888; width:350px; height:350px; overflow:auto; background:white; } ... if(!gadgets.log){ gadgets.log = function(msg){ var el = document.getElementById("log"); if(!el) return; var m = document.createElement("div"); m.innerHTML = msg; el.appendChild(m); } } In earlier chapters we’ve shown some techniques by making use of global functions.While convenient for small projects, it is good practice to namespace your script code.We’re going to encapsulate all of this in a static object namespace of TTT.AppDataPlay. Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- Turn-Based Games 127 1. Create the basic object skeleton and add it into your script source.The code is shown here: TTT.AppDataPlay = { } 2. Add a Keys object to this namespace that will encapsulate all the app data keys used for game play: TTT.AppDataPlay = { /** * Keys into the AppData store */ Keys: { activeGames: "activeGames", AppDataGetKey: "getData", finishedGames: "finishedGames" } } 3. Now we will add some empty object key definitions to hold our data. Even though JavaScript allows for dynamic creation of variables, it is good practice to explicitly declare and comment any variables that are used in multiple places. Add this code inside our TTT.AppDataPlay object: TTT.AppDataPlay = { . . . /** * Local cache of game app data for Viewer. */ myGameAppData: { "activeGames":null }, /** * Local cache of opponent game app data */ myOpponentAppData: {}, /** * Local cache of opponent in current game */ myOpponentCurrentGame: {}, Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- 128 Chapter 7 Flushing and Fleshing: Expanding Your App and Person-to-Person Game Play /** * Current opponent being played against */ currentOpponentId: null, . . . } 4. Add method stubs for game play support.These functions are set up as placehold- ers so that our code won’t break as we build it out.We will fill in the functions with real logic later. TTT.AppDataPlay = { . . . /** * Retrieves the gameInfo object for the specified opponent. * If one doesn't exist, a new gameInfo object is created. */ getCurrentGameObject: function(opponentId){}, /** * Convenience method to save the data currently in * this.myGameAppData to the backing app data store. */ updateStoredGameObject: function(){}, /** * Loads the specified gameInfo object into * window.currentGame and updates the board */ loadGame: function(gameInfo){}, /** * Displays a message regarding game play */ updateGameStatusMessage: function(message){}, /** * Serializes the board information into a string for storage */ Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- Turn-Based Games 129 getGameMovesString: function(game){}, . . . } 5. Add the methods for retrieval and saving of the Viewer’s app data.These methods are adaptations of what we wrote in Chapter 4, Persisting Information.The only real difference is that we include them in our TTT.AppDataPlay namespace. TTT.AppDataPlay = { . . . /** * Retrieve the current Viewer's app data. * This is an upgrade of the method introduced in * Chapter 4, Persisting Information. */ getMyAppData: function (){ var idparams = {}; idparams[opensocial.IdSpec.Field.USER_ID] = opensocial.IdSpec.PersonId.VIEWER; idparams[opensocial.IdSpec.Field.NETWORK_DISTANCE] = 0; idparams[opensocial.IdSpec.Field.GROUP_ID] = opensocial.IdSpec.GroupId.SELF; var id = opensocial.newIdSpec(idparams); var req = opensocial.newDataRequest(); req.add( req.newFetchPersonAppDataRequest(id, "*"), TTT.AppDataPlay.Keys.AppDataGetKey); req.send(function(data){ TTT.AppDataPlay.loadAppDataCallback(data, TTT.AppDataPlay.myGameAppData); }); }, /** * Sets an AppData key for the current Viewer * @param {String} key * @param {JSON object} value */ setAppDataKey: function (key, value) { var req = opensocial.newDataRequest(); Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- 130 Chapter 7 Flushing and Fleshing: Expanding Your App and Person-to-Person Game Play req.add(req.newUpdatePersonAppDataRequest( opensocial.IdSpec.PersonId.VIEWER, key, value), "set_" + key); req.send(); }, . . . } 6. Referenced in the methods just shown is a general-purpose app data callback handler adapted from Chapter 4, Persisting Information. Add the function loadAppDataCallback in as well.This function places all the AppData keys and values in the response into the specified object hash. In that way we make only one call for all the data. TTT.AppDataPlay = { . . . /** * Callback wrapper method for loadAppData. * This loads all the data into myLocalAppData * and triggers the loadedCallback function, if specified * @param {Object} data * @param {Function} loadedCallback */ loadAppDataCallback: function (data, targetCollection, loadedCallback){ logDataError(data); if (data.hadError()) { return; //Exit if nothing found } var mydata = data.get(TTT.AppDataPlay.Keys.AppDataGetKey).getData(); //App data has an additional layer of indirection. //We reassign mydata object to the data map object //Circumvent Viewer lookup by getting key var ok = false; for(var vkey in mydata){ mydata = mydata[vkey]; ok = true; break; } if(ok){ if(targetCollection == null){ gadgets.log("Bad collection in load callback"); Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- Turn-Based Games 131 targetCollection = {}; } var key; for(key in mydata){ targetCollection[key] = mydata[key]; } } if(loadedCallback && typeof(loadedCallback) == "function"){ loadedCallback(targetCollection); } }, . . . } 7. Finally, add in the function that retrieves a friend’s app data.We touched on this briefly in Chapter 4, Persisting Information, but did not write the code for retrieving other people’s app data.You can read, but cannot write, your friend’s data, provided the friend has the app installed as well.The main differences between this and Viewer are the idSpec used and the callback function. TTT.AppDataPlay = { . . . /** * Retrieve app data for an opponent. */ getFriendGameData: function (friendId){ var idparams = {}; idparams[opensocial.IdSpec.Field.USER_ID] = opensocial.IdSpec.PersonId.VIEWER; idparams[opensocial.IdSpec.Field.NETWORK_DISTANCE] = 1; if(!friendId){ friendId = window.friendPicker.selectedFriend.getId(); } idparams[opensocial.IdSpec.Field.GROUP_ID] = friendId; //If MySpace fixes the idSpec bug, use this /* if(!friendId){ friendId = window.friendPicker.selectedFriend.getId(); } idparams[opensocial.IdSpec.Field.USER_ID] = friendId; idparams[opensocial.IdSpec.Field.GROUP_ID] = opensocial.IdSpec.GroupId.SELF; */ Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- 132 Chapter 7 Flushing and Fleshing: Expanding Your App and Person-to-Person Game Play if(TTT.AppDataPlay.currentOpponentId != friendId && window.currentGame){ currentGame.clear(); } TTT.AppDataPlay.currentOpponentId = friendId; var id = opensocial.newIdSpec(idparams); var req = opensocial.newDataRequest(); req.add(req.newFetchPersonAppDataRequest(id, "*"), TTT.AppDataPlay.Keys.AppDataGetKey); req.send(function(data){ TTT.AppDataPlay.loadAppDataCallback(data, TTT.AppDataPlay.myOpponentAppData, TTT.AppDataPlay.selectedOpponentDataCallback); }); }, . . . } Warning You may notice a commented-out block of code within this function. At the time of this writing, MySpace had an inconsistency in its interpretation of the idSpec object from how other OpenSocial implementations interpret the same object. MySpace requires the user ID to always be VIEWER and the target friend ID to be placed in the GROUP_ID field. Other implementations put the target friend ID in the user ID field and use the reserved enum value opensocial.IdSpec.GroupId.SELF for the GROUP_ID value. 8. Stub out the callback function TTT.AppDataPlay. selectedOpponentDataCallback.This function holds much of the logic for game management. For now, we’re just going to stub it out and add a log state- ment so our code will run. TTT.AppDataPlay = { . . . selectedOpponentDataCallback: function(data){ gadgets.log("Got opponent data: " + data); } . . . } Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- Supporting Person-to-Person Game Play 133 At this point, we have the skeleton of the AppDataPlay object built and the utility methods required for communicating with the app data store of both players. Supporting Person-to-Person Game Play Thus far, our actual game engine (TTT.Game) has supported only human-versus-computer game play.We need to make a few tweaks to allow for dual-mode play (person-to-person, or P2P). In our game engine, we’ll always consider our opponent to be “Computer,” even when the opponent is another person.This saves us from a broader rewrite of the display logic and game engine. A simple flag value will give us enough information to differentiate computer from human opponents. Adding P2P Game Play Support in the Game Engine The first step is to make a few minor modifications to the original Tic-Tac-Toe game engine in order to support a human opponent in a turn-based system. We must change the “made a move” trigger to store data and wait instead of triggering a computer move. We also now need to handle cases when the game ends in a draw. 1. Add a property in the TTT.Game object to flag the game as being against a human opponent.This will be used in the game logic to trigger a computer move or trigger an app data store update. var TTT = { . . . Game: function(){ . . . /** * Flag to set if this is a vs. computer * or vs. human game */ this.isHumanOpponent = false; . . . } } 2. Add a new method to the TTT.Game prototype object to test for a draw. In human-versus-computer play we didn’t need to test for a draw. When no squares were available, there were no moves to make.With a human opponent we need to be able to identify the end of a game when there is no winner. Search for the lookForWin function and add the isADraw function immediately below. Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- 134 Chapter 7 Flushing and Fleshing: Expanding Your App and Person-to-Person Game Play /** * Checks to see if the current game is a draw (all cells filled) * */ isADraw : function(){ var openCell = false; for(var i=0; i < 9; i++){ if(!this.moves[i]){ openCell = true; break; } } return (!openCell); }, 3. Edit the game play event handler makePlayerMove to support P2P game play. Previously, this function would trigger a computer move if a winner was not discovered.We need to update this if statement with an else and include a check for isHumanOpponent. function makePlayerMove(e){ . . . if(!currentGame.hasWinner && !currentGame.isHumanOpponent){ window.setTimeout(function(){ currentGame.makeComputerMove(); }, 500); } else{ . . . } } 4. Add the game logic for taking a turn within the else statement in the makePlayerMove function from the preceding step.You will notice some new methods on the TTT.AppDataPlay object.We’ll fill in those methods in the next section. else{ // USER FEEDBACK MESSAGES HERE var adGame = TTT.AppDataPlay.getCurrentGameObject( TTT.AppDataPlay.currentOpponentId); adGame.moveNumber = adGame.moveNumber+1; if(currentGame.hasWinner){ msg = "You Win!"; Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- Supporting Person-to-Person Game Play 135 adGame.winner = window.viewer.getId(); } else{ msg += "Waiting for your opponent"; } adGame.boardLayout = TTT.AppDataPlay.getGameMovesString(currentGame); adGame.currentPlayer = TTT.AppDataPlay.currentOpponentId; TTT.AppDataPlay.updateStoredGameObject(adGame); if(currentGame.isADraw()){ msg += "IT'S A DRAW!"; } else if(!currentGame.hasWinner && !currentGame.isADraw()){ //Wait for next move } TTT.AppDataPlay.updateGameStatusMessage(msg); } Adding User Feedback Feedback to the user is an important part of the game play. In turn-based game play, sometimes it’s simply just not the Viewer’s turn.Without feedback the Viewer has the impression that the game is broken. When our Tic-Tac-Toe game is in P2P game play mode instead of P2C (person- to-computer), the computer takes on the role of commentator/narrator instead of opponent. Since this is a game, we like to add some sass to the commentary. Feel free to customize the messages to your liking. 1. Above the table with an ID of gameboard, add a div element with an ID of gameStatus.This is where the feedback messages are displayed. 2. In the makePlayerMove function, add a check and associated message to short-circuit processing if it is not the Viewer’s turn to make a move. Place this code immediately after the line where the var cell = ... statement is located. if(currentGame.isHumanOpponent && (currentGame.currentPlayer == TTT.Players.Computer)){ TTT.AppDataPlay.updateGameStatusMessage( "YO! I said you have to wait your turn."); return; } Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- 136 Chapter 7 Flushing and Fleshing: Expanding Your App and Person-to-Person Game Play 3. In the makePlayerMove function, add a random message generator within the else block that is executed during P2P game play. In our case we’ll create three messages and use a modulo operation coupled with a timestamp to pick a message in a quasi-random fashion.To the end user this will appear as a running commen- tary by the computer. var quotes = ["Nice Move!", "...interesting strategy", "What were you thinking?"]; var d = new Date(); var ndx = d.getMilliseconds() % 3; var msg = quotes[ndx]; //Random quote Modulo Magic Modulo operations can be one of the most useful bits of programming pixie dust to put in your bag of tricks. A modulo operation calculates the remainder after a division opera- tion between two numbers. To illustrate, if we divide the number 5 by 2, it divides evenly into two parts with 1 left over. The modulo (or “mod”) operation returns the 1. 5/2 = 2 with remainder of 1, or 5 mod 2 = 1 In JavaScript the statement would be written as 5 % 2. By far the most common usage is for displaying rows with alternating colors. This is accomplished with the following if statement: for(var i=0; i
- Supporting Person-to-Person Game Play 137 e.target.getAttribute("gamecell") : e.srcElement.getAttribute("gamecell"); if(currentGame.isHumanOpponent && (currentGame.currentPlayer == TTT.Players.Computer)){ TTT.AppDataPlay.updateGameStatusMessage( "YO! I said you have to wait your turn."); return; } currentGame.makeMove(TTT.Players.Human, cell); if(!currentGame.hasWinner && !currentGame.isHumanOpponent){ window.setTimeout(function(){ currentGame.makeComputerMove(); }, 500); } else{ var quotes = [ "Nice Move!", "...interesting strategy", "What were you thinking?"]; var d = new Date(); var ndx = d.getMilliseconds() % 3; var msg = quotes[ndx]; //Random quote var adGame = TTT.AppDataPlay.getCurrentGameObject( TTT.AppDataPlay.currentOpponentId); adGame.moveNumber = adGame.moveNumber+1; if(currentGame.hasWinner){ msg = "You Win!"; adGame.winner = window.viewer.getId(); } else{ msg += "Waiting for your opponent"; } adGame.boardLayout = TTT.AppDataPlay.getGameMovesString(currentGame); adGame.currentPlayer = TTT.AppDataPlay.currentOpponentId; if(currentGame.isADraw()){ msg += "IT'S A DRAW!"; adGame.winner = -1; } else if(!currentGame.hasWinner && !currentGame.isADraw()){ Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- 138 Chapter 7 Flushing and Fleshing: Expanding Your App and Person-to-Person Game Play TTT.AppDataPlay.startGameplayPolling(); } TTT.AppDataPlay.updateStoredGameObject(); TTT.AppDataPlay.updateGameStatusMessage(msg); } } Fleshing Out P2P Game Logic We have now created all the underpinnings of the game play. Now it’s time to glue it all together and implement our logic flows from the beginning of this chapter. Logic for Selecting an Opponent and Loading the Game Selecting an opponent and activating a game constitute the bulk of the workflow outlined at the beginning of the chapter.The app has to get both the Viewer’s and the opponent’s game data, reconcile which version is the latest, and determine whose move it is. If there is no active game, a new one is created. Find the function selectedOpponentDataCallback in the TTT.AppDataPlay object namespace that we stubbed out earlier.This is a big function, so we will discuss each section of it in turn. The first section of this function retrieves the GameInfo object for the two associated players by examining all the games stored in the selected opponent’s app data store until it finds one where the current Viewer’s ID matches the opponentId value in the GameInfo object.This object is then assigned to the variable TTT.AppDataPlay. myOpponentCurrentGame. At the very end of this section the code block similarly retrieves the GameInfo object associated with this opponent from the Viewer’s local copy of the app data store by calling getCurrentGameObject. if(!data){ return; } //Since we're in a callback, "this" has lost scope var thisObj = TTT.AppDataPlay; var myId = window.viewer.getId(); thisObj.myOpponentCurrentGame = null; var opponentStoredGames = data[thisObj.Keys.activeGames]; if(opponentStoredGames != undefined && opponentStoredGames != null){ opponentStoredGames = gadgets.json.parse(opponentStoredGames); if(opponentStoredGames.length){ var curGame; for(var i=0; i < opponentStoredGames.length; i++){ Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- Supporting Person-to-Person Game Play 139 curGame = opponentStoredGames[i]; if(curGame.opponentId == myId){ thisObj.myOpponentCurrentGame = curGame; break; } } } } var msg = "Playing: " + thisObj.currentOpponentId; var myGameInfo = thisObj.getCurrentGameObject(thisObj.currentOpponentId); The second section of the selectedOpponentDataCallback function reconciles the two GameInfo objects. It does this by identifying which object has the latest move. The boardLayout is then copied into the Viewer’s GameInfo object to ensure that the Viewer’s copy is the latest copy. var latestGameInfo; if(thisObj.myOpponentCurrentGame != null){ //Reconcile to latest game version if(myGameInfo.moveNumber
- 140 Chapter 7 Flushing and Fleshing: Expanding Your App and Person-to-Person Game Play else if(latestGameInfo.winner == -1){ thisObj.updateGameStatusMessage("ITS A DRAW!!!"); } else{ thisObj.updateGameStatusMessage("YOUR OPPONENT WON!!!"); } //debugger; thisObj.loadGame(latestGameInfo); thisObj.markGameFinished(latestGameInfo.gameId); thisObj.cancelPolling(); return; } thisObj.loadGame(latestGameInfo); if(latestGameInfo.currentPlayer==myId) { //My turn msg += "It is your turn"; } else{ msg += "Still waiting on opponent"; thisObj.startGameplayPolling(); } The preceding code describes what occurs when a game is discovered to be in progress.The second branch of this is what occurs when this is a new game or the opponent has not yet accepted the game. In that instance the Viewer’s game is loaded and appropriate user feedback is displayed. } else{ latestGameInfo = myGameInfo thisObj.loadGame(latestGameInfo); //Save the new game back if(latestGameInfo.moveNumber==0){ msg += "New game started - make your move"; } else{ msg += "Waiting for your opponent to move"; } } thisObj.updateGameStatusMessage(msg); That finishes the selectedOpponentDataCallback function.There were two other functions touched on in the preceding description: getCurrentGameObject and loadGame. Both methods are fairly simple. Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- Supporting Person-to-Person Game Play 141 The function getCurrentGameObject retrieves the GameInfo object from the Viewer’s app data store for the associated opponent. If there is no game in progress, a new GameInfo object is initialized for this opponent and returned. /** * Retrieves the gameInfo object for the specified opponent. * If one doesn't exist, a new gameInfo object is created. * */ getCurrentGameObject: function(opponentId){ var result = null; if(this.myGameAppData && this.myGameAppData.activeGames != null){ var items = this.myGameAppData.activeGames; var i; if(items.length){ for(i=0; i < items.length; i++){ if(items[i].opponentId == opponentId){ result = items[i]; break; } } } else{ //Is object var x; for(x in items){ if(items[x].opponentId == opponentId){ result = items[x]; break; } } } } if(result == null){ result = new TTT.GameInfo(opponentId); if(!this.myGameAppData.activeGames){ this.myGameAppData.activeGames = []; } this.myGameAppData.activeGames.push(result); } return result; } The function loadGame loads the GameInfo into the game board and initializes the window.currentGame object.The bulk of this method is display logic. In our Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- 142 Chapter 7 Flushing and Fleshing: Expanding Your App and Person-to-Person Game Play implementation, the Viewer’s moves are always represented by an X (TTT.Players. Human) and the opponent’s moves are always represented by an O (TTT.Players. Computer). The board layout is represented by a pipe-delimited list of moves.This represents the nine squares of the Tic-Tac-Toe board. Each move is either an X, an O, or nothing. Depending on the originating source of the GameInfo object (Viewer’s object or opponent’s object), as determined by matching the Viewer’s ID against gameInfo.dataSourceId, these moves are transcribed to be appropriate for the Viewer. For example, if the latest GameInfo object originated from the Viewer’s app data store, the X’s and O’s remain the same. If the latest GameInfo object was from the opponent’s app data store, the X’s and O’s have to be flipped. /** * Loads the specified gameInfo object into * window.currentGame and updates the board */ loadGame: function(gameInfo){ if(currentGame){ currentGame.clear(); } else{ currentGame = new TTT.Game(); } currentGame.isHumanOpponent = true; //Flag to not make computer moves var myId = window.viewer.getId(); var fromMe = (gameInfo.dataSourceId == myId); if(gameInfo.boardLayout){ var moves = gameInfo.boardLayout.split("|"); var nOneMark, nZeroMark; if(fromMe){ nOneMark = TTT.Players.Human; nZeroMark = TTT.Players.Computer; } else{ nOneMark = TTT.Players.Computer; nZeroMark = TTT.Players.Human; } //1 == game owner, 0 == viewer for(var i=0; i < moves.length; i++){ if(moves[i]==""){ continue; } else{ if(moves[i]=="1"){ Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
- Supporting Person-to-Person Game Play 143 currentGame.currentPlayer = nOneMark; currentGame.makeMove(nOneMark, i); } else{ currentGame.currentPlayer = nZeroMark; currentGame.makeMove(nZeroMark, i); } } } //Now init to current player //debugger; if(gameInfo.currentPlayer == myId){ currentGame.currentPlayer = TTT.Players.Human; } else{ currentGame.currentPlayer = TTT.Players.Computer; } } }, To wrap up this section, we’ll look at how the board is serialized into a string for storage in the app data store.The function getGameMovesString takes care of those details by interpreting the game board from currentGame and turning it into a pipe- delimited string. Remember that the opponent is always considered the computer. /** * Serializes the board information into a string for storage */ getGameMovesString: function(game){ var i=0; var result = ""; for(i=0; i < 9; i++){ if(game.moves[i] === undefined || game.moves[i] == "" || game.moves[i] == null){ result += "|"; } else{ if(game.moves[i] == TTT.Players.Computer){ result += "0|"; } else{ result += "1|"; } } Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
CÓ THỂ BẠN MUỐN DOWNLOAD
Chịu trách nhiệm nội dung:
Nguyễn Công Hà - Giám đốc Công ty TNHH TÀI LIỆU TRỰC TUYẾN VI NA
LIÊN HỆ
Địa chỉ: P402, 54A Nơ Trang Long, Phường 14, Q.Bình Thạnh, TP.HCM
Hotline: 093 303 0098
Email: support@tailieu.vn