Turn-based Game Design Help

Started by
6 comments, last by Xyran 14 years ago
I am have trouble creating a simple turn based multiplayer game. Simplified Game Spec: Each player has 3 actions they can perform; a0 a1 a2 Game starts and waits for player0 to act. player0's action is recorded and the game steps to the next player, player1, and waits for player1's action. player1's action is recorded. If player1 is the last player round0 is finished and round1 starts. Game steps back to player0 and waits for their action. This repeats for 3 rounds after which win conditions are evaluated and the winner is announced. The game then starts again. I have implemented player actions and winning conditions but having difficulties getting my head around implementing stepping through players and rounds. I have tried giving each player a isActive bool field and constantly checking for it and if it's true then it's that player's turn, if not then that player can't act but finding it difficult in C# Forms creating this. Can someone suggest the best way to achieve what I described and maybe point me in the right dirrection as to how to start implementing it. Thanks alot
Advertisement
Who 'owns' the Player objects? Let that code keep track of the active player, and check which one is next.

For example, if you have a Game class of some sort, which contains the list of players, it would keep a reference to the active player. Once that player submits his action, the game then sets the next player as the active player. If it was the last player in the list, the next round is started.
Create-ivity - a game development blog Mouseover for more information.
If you take a look at the rules for most boardgames (i.e., something from here), they'll have a numbered list of steps, or "phases", that each player transitions through during their turn. At each phase they can possibly make decisions and perform actions. Each one can also have a list of sub-phases. Your code for a turn-based game will closely mimic such a structure, where each phase of a turn has some state and logic associated with it that knows how to handle that phase. Once the player has gone through all phases for a turn, you switch active players and start a new turn.
Thanks for your help. I have created a State enum. Please take a look, feedback welcome. Would I create a "phase" class? Seems like sub-phases could inherit from that base class? Is that sound design?

 using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;namespace TurnBasedGamePlay{    public partial class Form1 : Form    {        public Game game = new Game();        public Form1()        {            InitializeComponent();            textBox.Text += game.rprt;        }        private void p1_Click(object sender, EventArgs e)        {            if (game.gameState == Game.State.P1)            {                game.p1.action();                game.changeState();                game.rprt += "Player1 took their turn" + Environment.NewLine;                  }            else if (game.gameState == Game.State.END)            {                game.rprt += "The Game is Over!" + Environment.NewLine;            }            else            {                game.rprt += "Player1 tried to play when it wasn't their turn!" + Environment.NewLine;            }            setAutoScroll();        }        private void p2_Click(object sender, EventArgs e)        {            if (game.gameState == Game.State.P2)            {                game.p1.action();                game.changeState();                game.rprt += "Player2 took their turn" + Environment.NewLine;            }            else if (game.gameState == Game.State.END)            {                game.rprt += "The Game is Over!" + Environment.NewLine;            }            else            {                game.rprt += " Player2 tried to play when it wasn't their turn!" + Environment.NewLine;            }            setAutoScroll();        }        private void startButton_Click(object sender, EventArgs e)        {             game.setStartState();            setAutoScroll();            startButton.Enabled = false;            endButton.Enabled = true;        }        public void setAutoScroll()        {            textBox.Text = game.rprt;            textBox.SelectionStart = textBox.Text.Length;            textBox.ScrollToCaret();            textBox.Refresh();        }        private void endButton_Click(object sender, EventArgs e)        {            game.setEndState();            setAutoScroll();            endButton.Enabled = false;            startButton.Enabled = true;        }    }}    public class Game    {        public enum State { START, P1, P2, END }        public State gameState;        public string rprt;        private string currentStateMsg;        public Player p1 = new Player();        public Player p2 = new Player();         public Game()        {            rprt = "Game Constructed." + Environment.NewLine;            currentStateMsg = "State changed. Current State is: ";        }        public string report()        {            rprt += gameState.ToString() + Environment.NewLine;            return rprt;        }        public void setStartState()        {            gameState = State.P1;            rprt += "Game Started! " + currentStateMsg + gameState.ToString() + Environment.NewLine;        }        public void setEndState()        {            gameState = State.END;            rprt += "Game Finished. " + currentStateMsg + gameState.ToString() + Environment.NewLine;        }        public void changeState()        {            if (gameState == State.START)            {                gameState = State.P1;                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P1)            {                gameState = State.P2;                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P2)            {                gameState = State.P1;                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else            {                rprt += "Something went wrong! Current State is: " + gameState.ToString() + Environment.NewLine;            }        }    }    public class Player    {        public string Name { get; set; }        public int MyInt { get; set; }               public Player()        {                    }        public void action()        {            //do stuff        }    }


Thanks

EDIT: Source tags!
In the case of most turn based games, the overall game state is basically whichever players turn it currently is. This might be an enum if you are absolutely certain that there will never be more than a particular number of players. You might also have it as an index into an array of players. Some games have some actions where something happens after all players have moved for a round.

If a turn has phases, then it might be better described as a "Turn State" of that player's turn. Ergo, while the game has a game state of whose turn it is and some other game level concepts like that, each player's turn can have a similar state variable that governs the state of their turn.

In other words, the game as a whole is a finite state machine, and the player is also a finite state machine. You don't have to use the overall game state to track turn phase.

Get off my lawn!

An index into an array of players seems like a better option. Allowing me to change the number of players easily if need be.

However my changeState() method assumes a set number of players. (In my code, 2 players)

How would I change Game State with an unknown number of players? I have got the behaviour using a PlayerCount and StateCount (increases by 1 after each state change) but there must be a more elegant way to do it? It handles up to 5 players. Increasing the number of players though to say 10 would be a pain and alot of work.

It's one ugly method and I can't quite follow what I did even though I wrote it! So feel to ask any questions. I feel I am quite comfortable with the c# language but really struggling in creating elegant smart algorithms and general design of my projects.

public void changeState()        {            if (gameState == State.START)            {                gameState = State.P1;                                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P1)            {                gameState = State.P2;                StateCounter++;                 rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P2 && PlayerCount == 2 && StateCounter == 8)            {                gameState = State.END;                       rounds = Rounds.END;                    rprt += "Bet Round State: (Should be END)" + rounds.ToString() + Environment.NewLine;                    StateCounter = 0;                     rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P2 && PlayerCount == 2 && StateCounter == 6)            {                gameState = State.P1;                                    rounds = Rounds.ROUND4;                    rprt += "Bet Round State: (Should be ROUND4)" + rounds.ToString() + Environment.NewLine;                    StateCounter++;                     rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P2 && PlayerCount == 2 && StateCounter == 4)            {                gameState = State.P1;                                    rounds = Rounds.ROUND3;                    rprt += "Bet Round State: (Should be ROUND3)" + rounds.ToString() + Environment.NewLine;                    StateCounter++;                     rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P2 && PlayerCount == 2)            {                gameState = State.P1;                                    rounds = Rounds.ROUND2;                    rprt += "Bet Round State: (Should be ROUND2)" + rounds.ToString() + Environment.NewLine;                    StateCounter++;                     rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }              else if (gameState == State.P2)            {                gameState = State.P3;                                    StateCounter++;                     rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P3 && PlayerCount == 3 && StateCounter == 12)            {                gameState = State.END;                rounds = Rounds.END;                rprt += "Bet Round State: (Should be END)" + rounds.ToString() + Environment.NewLine;                StateCounter = 0;                 rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P3 && PlayerCount == 3 && StateCounter == 9)            {                gameState = State.P1;                                    rounds = Rounds.ROUND4;                    rprt += "Bet Round State: (Should be ROUND4)" + rounds.ToString() + Environment.NewLine;                    StateCounter++; //                    rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P3 && PlayerCount == 3 && StateCounter == 6)            {                gameState = State.P1;                                    rounds = Rounds.ROUND3;                    rprt += "Bet Round State: (Should be ROUND3)" + rounds.ToString() + Environment.NewLine;                    StateCounter++; //                    rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P3 && PlayerCount == 3)            {                gameState = State.P1;                rounds = Rounds.ROUND2;                rprt += "Bet Round State: (Should be ROUND2)" + rounds.ToString() + Environment.NewLine;                    StateCounter++; //4                    rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P3 && StateCounter == 10)            {                rounds = Rounds.ROUND3;                rprt += "Bet Round State: (Should be ROUND3)" + rounds.ToString() + Environment.NewLine;                StateCounter++;                rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                gameState = State.P4;                                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P3 && StateCounter == 15)            {                rounds = Rounds.ROUND4;                rprt += "Bet Round State: (Should be ROUND4)" + rounds.ToString() + Environment.NewLine;                StateCounter++;                 rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                gameState = State.P4;                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P3 && StateCounter == 20)            {                //End - Evaluate Winner                gameState = State.END;                rounds = Rounds.END;                rprt += "Bet Round State: (Should be END) " + rounds.ToString() + Environment.NewLine;                StateCounter = 0;                rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P3)            {                gameState = State.P4;                                    StateCounter++;                     rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P4 && PlayerCount == 4 && StateCounter == 16)            {                gameState = State.END;                rounds = Rounds.END;                rprt += "Bet Round State: (Should be END) " + rounds.ToString() + Environment.NewLine;                StateCounter = 0;                rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P4 && PlayerCount == 4 && StateCounter == 12)            {                gameState = State.P1;                rounds = Rounds.ROUND4;                rprt += "Bet Round State: (Should be ROUND4) " + rounds.ToString() + Environment.NewLine;                StateCounter++;                rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P4 && PlayerCount == 4 && StateCounter == 8)            {                gameState = State.P1;                rounds = Rounds.ROUND3;                rprt += "Bet Round State: (Should be ROUND3) " + rounds.ToString() + Environment.NewLine;                StateCounter++;                rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P4 && PlayerCount == 4)            {                gameState = State.P1;                rounds = Rounds.ROUND2;                rprt += "Bet Round State: (Should be ROUND2) " + rounds.ToString() + Environment.NewLine;                    StateCounter++;                     rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P4)            {                gameState = State.P5;                                    StateCounter++;                     rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P5 && StateCounter == 5)            {                rounds = Rounds.ROUND2;                rprt += "Bet Round State: (Should be ROUND2) " + rounds.ToString() + Environment.NewLine;                StateCounter++;                rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                gameState = State.P4;                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }            else if (gameState == State.P5)            {                gameState = State.P1;                                    StateCounter++;                     rprt += "StateCounter: " + StateCounter.ToString() + Environment.NewLine;                                rprt += currentStateMsg + gameState.ToString() + Environment.NewLine;            }                        else            {                rprt += "Game Ended! " + gameState.ToString() + Environment.NewLine;            }        }


Thanks
If you want a variable number players you need to make things as general as possible. One way to do that is to index your players through a List.

Here's one way you can do this:
 public class Game{    private List<Player> players;    private Player currentPlayer;    public void InitializeGame(int numOfPlayers)    {        players = new List<Player>();        // Create an instance of the player class for each player        for(int i=0; i < numOfPlayers; i++)        {             players.add(new Player());        }        currentPlayer = players[0];    }} 


Within your change state function you can now reference the player by simply checking who the current player is.
After reading over your posts a bit more, I would suggest making the GameState not contain the player numbers. Instead try something like this: (Add this to my previous code)

public class Game{    public enum GameState { START, MIDROUND, END }    public GameState gameState;    private List<Player> activePlayers; // Players left in the round    private List<Player> players; // All players in the game    private Player currentPlayer;    private int round;    private const int maxRounds = 3;    public void InitializeGame(int numOfPlayers)    {        players = new List<Player>();        activePlayers = new List<Player>();        // Create an instance of the player class for each player        for(int i=0; i < numOfPlayers; i++)        {             Player player = new Player();             players.add(player);        }        // Set the necessary variables for a new game        currentPlayer = players[0];        gameState = GameState.START;        round = 0;        // Start the game here        changeState();    }    public void changeState()    {        if(gameState == GameState.START)        {            // Begins each round by incrementing the round number            round++;            // Refill the active players for the round            foreach(Player player in players)                 activePlayers.add(player);            // Check if the maximum rounds has been reached            if(rounds > maxRounds)            {                gameState = GameState.END;                changeState();            }             else                gameState = GameState.MIDROUND;                                }        else if(gameState == GameState.MIDROUND)        {            activePlayers.RemoveAt(0);            // Check if all players have played the round            if(activePlayers.Count == 0)            {                gameState = GameState.START;                changeState();            }            else            {                currentPlayer = activePlayers[0];            }        }        else if(gameState == GameState.END)        {             // Determine winner here        }    }} 


On each button click, make sure its the right player and then call changeState().

(Note: made a few changes)

[Edited by - Xyran on March 18, 2010 9:15:02 PM]

This topic is closed to new replies.

Advertisement