Lecture 2: Monday afternoon Designing Interactive Games with the universe.ss Teachpack ---------------------------------------------------------- Suppose we want to write a program that allows two players to play a simple card game of war. Each player starts with 26 random cards. On each turn a player show the top card in his deck. An external observer then compares the card and the player with the better card gets both cards. On tie, we give every player back their card - to make the rules simpler. The game ends when one of the players has no more cards. How can we program this simple game? The rules for each player are the same, and so one program should be used to model either one of the players. The arbiter, the game administrator, waits to get both players' top card information, then informs each player of the outcome: either the player receives both cards played, or the player receives no cards. The game administrator can tell when the game is over, as it can keep track of the number of cards each player has in hand. The administrator and the players need to communicate with each other. To do so, they exchange messages. We describe the progress of these types of programs using sequence diagrams as follows: Player 1: Player 2: Administrator ------------------------------------------------------ start listening for messages I want to play:-----------------------> record the first player | v save your deck<------------------------ here is your deck | | I want to play:-----> record the second player | | v | save your deck<------- here is your deck v | My card: -----------------------------> record the card | v | | My card:------------> compare two cards | | v | | if not end? save the cards<---------------------+-- you win two cards | | | v | do nothing<-------+-- you lost your card | v | | My card:------------> record the card v | My card: -----------------------------> compare two cards ... | My card:------------> compare two cards | | v | | if end? celebrate<--------------------------+-- you win | | | v | cry<--------------+-- you loose disconnect the players The administrator is called 'THE UNIVERSE' and represents a server, the players are the 'WORLD's that the universe manages, known as its clients. World that interacts with the Universe: --------------------------------------- Let us first see what the player needs to do. The player starts with an empty hand, asks the universe to join in the game, receives the initial deck of cards, then starts the play one turn at a time. Each turn starts with a message to the universe showing the top card, and ends with a message from the universe sending back the cards the player has won (two cards, or none). So, the player world needs to be able to register for the game, send a message to the universe, and receive a message from the universe. To handle this the 'bigbang' for the world is extended with two new clauses: (on-receive receive-message) (register 'IP-address') and the functions 'on-key-event', 'on-tick-event', and 'on-mopuse-event' that in the past produced a new state of the 'world' can now produce a 'package' that contains both the new state of the world and a message to be sent to the 'universe'. The message contents can be anything we wish it to be, as long as the data representation is a plain S-Expression. In our case, we will produce a 'package' when the player hits the space bar, i.e. within the 'on-key-event' function. The message we will send to the 'universe' will be a 'String' that represents the card we play in this turn. The 'universe needs to send us two or none cards that we have won in this turn. The easiest way to handle this is by sending a list of cards: an empty list has no cards, which makes sense. The Universe: ------------- The data definition for the 'universe' consists of two parts. One part is always the list of the 'world's that are currently connected to the 'universe'. The second part represents the state of the game --- any information that the 'universe' needs to know to administer the game, the communications with all 'world's. When the universe performs some of its actions, such as handling a request of a new 'world' to join the game, or processing a message from one of the 'world's, it will typically change the state of the 'universe', including possibly changing the list of 'world's that are currently connected. But, there are two other things that the 'universe' may want to do at the same time: send new messages to one or more 'world's and disconnect one or more 'world's. A new data definition, a 'bundle' represents a collection of these three outcomes: the new state of the 'universe', a list of messages to send, and a list of 'world's that are to be disconnected. So, once we define the initial state of the 'universe', we start the program and make the server available to receive communications be running: (universe init-universe-data (on-new add-world) (check-with pred?) (on-disconnect disconnect-world) (on-msg process)) where the function 'add-world' consumes the state of the 'universe' and the 'world' that is requesting to join and produces a 'bundle'; the function 'process' consumes the current state of the 'universe' the 'world' that has sent the message, and the message itself, ad, again, produces a 'bundle'. The 'disconnect-world' function consumes the current state of the 'universe' and the 'world' that is being disconnected, and produces a 'bundle'. Of course, the world that just disconnected in included in the list of 'world's to disconnect. Finally, the function 'pred?' is optional. It is a predicate, a function that verifies that the state of the 'universe' is indeed correctly formed. We defer the discussion of the details of dealing with these functions to the example provided in the lab notes.