Hello everyone! I finally decided to pick up my C# book again, and decided to try a shot at creating tic-tac-toe ala ConsoleApp. I haven't actually read any material on game design itself or how to approach something such as this, I just used the knowledge I gained of the C# programming language to solve the "tic-tac-toe problem." If there is one thing I learned from this though, is that a preliminary design would have made my life easier creating it (especially calculating if anyone won). Any comments, criticisms or even compliments would be greatly appreciated, thanks!
How to play:
When prompted to enter a position, you enter two numbers, eg. "12"
1 is the column, 2 is the row
The top left block starts at 00
Numbers are "drawn" to the console window for the grid as guidance during play.
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TicTacToe
{
class Program
{
static void Main(string[] args)
{
Program prog = new Program();
try
{
prog.Entrance();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private void Entrance()
{
Console.WriteLine("Welcome to Tic-Tac-Toe!");
GameLoop();
}
private void GameLoop()
{
Game game = new Game();
while (!game.GameOver)
{
game.PlayRound();
}
}
}
}
Game.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TicTacToe
{
partial class Game
{
public Game()
{
board = new GameBoard();
}
public bool GameOver
{
get { return gameOver; }
}
public void PlayRound()
{
Console.Clear();
board.Draw();
MakeMove('X');
if (CalculateWins(board))
return;
Console.Clear();
board.Draw();
MakeMove('O');
CalculateWins(board);
}
// returns true if someone won, otherwise false
private bool CalculateWins(GameBoard board)
{
char[,] sectors = board.Sectors;
char token = ' ';
int count = 0;
// loop through the columns
for (int i = 0; i < sectors.Length / 3 && count < 3; i++)
{
if (sectors[i, 0] != ' ') // check if column has a token to start with
{
token = sectors[i, 0];
// first column check
if (i == 0)
{
// vertical from 0,0
for (int y = 0; y < sectors.Length / 3; y++)
{
if (sectors[i, y] == token)
count++;
}
if (count < 3)
{
count = 0;
//horizontal from 0,0
for (int y = 0; y < sectors.Length / 3; y++)
{
if (sectors[y, 0] == token)
count++;
}
}
if (count < 3)
{
count = 0;
//diagonal from 0,0
for (int y = 0, b = 0; y < sectors.Length / 3; y++, b++)
{
if (sectors[b, y] == token)
count++;
}
}
if (count < 3)
count = 0;
} // end first column check
// second column check
if (i == 1)
{
for (int y = 0; y < sectors.Length / 3; y++)
{
if (sectors[i, y] == token)
count++;
}
if (count < 3)
count = 0;
} // end second column check
// third column check
if (i == 2)
{
for (int y = 0; y < sectors.Length/3; y++)
{
if (sectors[i, y] == token)
count++;
}
if (count < 3)
count = 0;
} // end third column check
}
} // end column loop
// begin row loop starting at second row, if the count is less than 3
for (int i = 1; i < sectors.Length / 3 && count < 3; i++)
{
if (sectors[0,i] != ' ') // check if row has a token to begin with
{
token = sectors[0,i];
// second row check
if (i == 1)
{
//check horizontally
for (int y = 0; y < sectors.Length / 3; y++)
{
if (sectors[y, i] == token)
count++;
}
if (count < 3)
count = 0;
}
// third row check
if (i == 2)
{
// check horizontally
for (int y = 0; y < sectors.Length/3; y++)
{
if (sectors[y,i] == token)
count++;
}
// next try diagonally to top right
if (count < 3)
{
count = 0;
for (int y = 0, b = 2; y < sectors.Length / 3; y++, b--)
{
if (sectors[y,b] == token)
count++;
}
}
if (count < 3)
count = 0;
} // end third row check
}
} // end row loop
if (count == 3)
{
Console.Clear();
board.Draw();
Console.WriteLine("{0} won!", token);
gameOver = true;
return true;
}
return false;
}
private void MakeMove(char token)
{
bool invalidMove;
int x = 0, y = 0;
do
{
Console.Write("{0}, make your move: ", token);
invalidMove = false;
string input = Console.ReadLine();
try
{
if (input.Length != 2)
throw new FormatException("Need two numbers");
x = int.Parse(input.Substring(0, 1));
y = int.Parse(input.Substring(1, 1));
}
catch (FormatException fex)
{
Console.WriteLine(fex.Message);
invalidMove = true;
continue;
}
if (x > 2 || x < 0 || y > 2 || y < 0 || !board.CheckPosition(x, y))
{
Console.WriteLine("Invalid position!");
invalidMove = true;
}
} while (invalidMove);
board.AddPiece(x, y, token);
}
bool gameOver = false;
private GameBoard board;
}
}
GameBoard.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TicTacToe
{
partial class Game
{
private class GameBoard
{
public void Draw()
{
Console.WriteLine(" 0 1 2\n0{0}|{1}|{2}\n -----\n1{3}|{4}|{5}\n -----\n2{6}|{7}|{8}",
sectors[0, 0], sectors[1, 0], sectors[2, 0],
sectors[0, 1], sectors[1, 1], sectors[2, 1],
sectors[0, 2], sectors[1, 2], sectors[2, 2]);
}
public void AddPiece(int x, int y, char type)
{
if (type != 'X' && type != 'O')
throw new FormatException("parameter \"type\" of AddPiece must be either 'X' or 'O'");
sectors[x, y] = type;
}
// sets the character to ' ' to "remove" a gamepiece
// currently unused, will be used later for asking if player
// wants another game
public void RemovePiece(int x, int y)
{
sectors[x, y] = ' ';
}
// checks if a position is available
public bool CheckPosition(int x, int y)
{
if (sectors[x, y] != ' ')
return false;
return true;
}
// ability to retrieve current sector status, used for calculating wins
// returns a clone so that the actual array is still encapsulated, otherwise
// we would be returning a reference (technically, anyhow)
public char[,] Sectors
{
get { return (char[,])sectors.Clone(); }
}
private char[,] sectors = { { ' ', ' ', ' ' }, { ' ', ' ', ' ' }, { ' ', ' ', ' ' } };
}
}
}
After a couple hours of debugging and tweaking, it seems to work flawlessly with elegant exception handling(I hope). Thank you again for checking out my work. [smile]
[Edited by - dmreichard on June 19, 2008 6:57:37 AM]