DonAman
Joined: 27 Nov 2011 Posts: 4
|
Posted: Tue Dec 06, 2011 6:08 am Post subject: Connect 4 tutorial |
|
|
As 2nd year students in ENSICAEN (a french engineering school) we are supposed to work on a one-year project which we must choose the topic from a given list. We had the opportunity to choose a project concerning the YAGE 3D engine, beeing supervised by 'Anarky' on this forum. As a first step, we have been asked to write some tutorials to help beginners to create a simple D program...
Please don't hesitate to express your suggestions about this tutorial in order to be able to improve it.
______________________________________________________________________________________
Here is a small tutorial to develop a 'Connect4' game in D language.
To do this, we will use the Tango library.
D is an object-oriented language, so we create a class 'Connect4' that
will contain all the necessary to model the game:
attributes:
-a matrix 7*6 filled with characters that represent the game board itself
-the current player (2 possible values)
methods:
-switch player: method that switches from player 1 to player 2 or player 2 to player 1
-addPawn: method that adds a pawn in a column of the game
-isPlayable: indicates if the selected column is playable: it checks if the column where to play exists and if it's not full already.
-draw: checks if it's still possible to add pawns in the game.
-play: plays the whole game until someone win or until all cells are filled with pawns.
Here is the code with descriptive comments:
Code: |
import tango.io.Stdout;
import tango.stdc.stdio;
/*Change it if you want to specify a different board size*/
const int WIDTH = 7;
const int HEIGHT = 6;
/*definition of directions, used as iterators to check a winning move: CHK_DIR = [a,b] where a and b are respectively horizontal and vertical elementary displacements:*/
/*to move on a same row (row number is untouched)*/
const int[2] CHK_ROW = [0,1];
/*to move on a same col (col number is untouched)*/
const int[2] CHK_COL = [1,0];
/*to move on a ascending diagonal*/
const int[2] CHK_DIAG_ASC = [-1,1];
/*to move on a descending diagonal*/
const int[2] CHK_DIAG_DSC = [1,1];
/*class describing the whole game*/
class Connect4 {
private:
/*represents the board*/
char m[HEIGHT][WIDTH];
/*the current player*/
char player;
/*to switch from a player to another */
void switchPlayer(){
if (player == 'x'){
player = 'o';
}
else {
player = 'x';
}
}
/*Checks if at least 4 pawns are aligned among the 4 possible directions*/
bool isWinningMove(uint row,uint col,char player){
/*Checks if a least 4 pawns are aligned for a specific direction (Nested function are allowed in D)*/
bool check(int[] direction){
/*moves and counts forwards in the given direction*/
int nextRow = row+direction[0];
int nextCol = col+direction[1];
/*counts the pawn that has just been added*/
int total = 1;
/*while we stay in the bounds of the board and while the neighbor in the given direction belongs to the current player, we keep on counting*/
while(nextCol<WIDTH && nextRow<HEIGHT && nextRow>= 0 &&
m[nextRow][nextCol] == player){
total++;
nextRow+= direction[0];
nextCol+= direction[1];
}
/*moves and counts backwards in the given direction*/
nextRow = row-direction[0];
nextCol = col-direction[1];
while(nextRow<HEIGHT && nextCol>= 0 && nextRow>= 0 &&
m[nextRow][nextCol] == player){
total++;
nextRow-= direction[0];
nextCol-= direction[1];
}
return total>= 4;
}
/*Returns true if at least 4 pawns are aligned among the 4 possible directions*/
return check(CHK_ROW)||check(CHK_COL)||check(CHK_DIAG_ASC)||check(CHK_DIAG_DSC);
}
/*Displays the board*/
void display(){
/*upper delimiter*/
for(uint j = 0;j<WIDTH;j++){
Stdout("--");
}
Stdout.newline();
/*The board itself*/
for(uint i = 0;i<HEIGHT;i++){
for(uint j = 0;j<WIDTH;j++){
Stdout(m[i][j]~" ");
}
Stdout.newline();
}
/*lower delimiter*/
for(uint j = 0;j<WIDTH;j++){
Stdout("--");
}
Stdout.newline();
/*display column labels*/
for(uint j = 0;j<WIDTH;j++){
Stdout.format("{} ",j);
}
Stdout.newline();
}
/*Adds a pawn to a given column*/
bool addPawn(uint col,char player){
uint i = 0;
/*The pawn is going down until it can't go further*/
while(i<HEIGHT && m[i][col] == '.'){
i++;
}
i--;
/*Puts the pawn*/
m[i][col] = player;
/*Checks if it's a winning move*/
return isWinningMove(i,col,player);
}
/*Checks if a column is playable (must be in bounds and not fully filled up)*/
bool isPlayable(uint col){
return (col>= 0 && col<WIDTH && m[0][col] == '.');
}
/*Checks if it's still possible to add a pawn on the board*/
bool draw(){
for(int j = 0;j<WIDTH;j++){
/*if a top row containing a free cell exists, the game continues*/
if(m[0][j] == '.'){
return false;
}
}
/*if none of the top rows contains a free cell, then the game is a draw*/
return true;
}
public:
/*Constructs the board of the game and initializes variables*/
this(){
/*the first player to play is player 'x'*/
player = 'x';
for(int i = 0;i<HEIGHT;i++){
for(int j = 0;j<WIDTH;j++){
/*empty cells are filled with '.'*/
m[i][j] = '.';
}
}
}
/*Plays the whole game until someone win or until all cells are filled up with pawns.*/
void play(){
/*will contain the column number where the player wants to play (read from input stream). Initialization with an out-of-bound value*/
uint colToPlay = WIDTH;
/*no winner at the beginning*/
char winner = '_';
/*While it's still possible to play and nobody wins:*/
while(!draw() && winner == '_'){
/*Tries to read the column where to play from the input buffer until the user make a possible choice: */
do{
display();
Stdout.format("Player '"~player~"' plays: enter a column number where to play.").newline();
scanf("%u",&colToPlay);
/*to clean the input buffer (in case of failure)*/
scanf("%*[^\n]");
getchar();
if(!isPlayable(colToPlay)){
Stdout.format("This is not a valid move, please chose an other column where to play.").newline();
}
}
while(!isPlayable(colToPlay));
/*if the move leads to a victory:*/
if (addPawn(colToPlay,player)){
/*current player is the winner*/
winner = player;
}
else{
/*else we can switch players*/
switchPlayer();
}
}
display();
/*if still no winners a this point, it means we exited the main loop because of a draw game:*/
if(winner == '_'){
Stdout("Draw!").newline();
}
else{
Stdout.format("Player '{}' won!",winner).newline();
}
}
}
/*let's play!*/
int main(){
Connect4 c4 = new Connect4();
c4.play();
return 0;
}
|
|
|