Program programming training: Tic-tac-toe implementation

Source: Internet
Author: User

Output page:


Tic-tac-toe source code:

/* * File: tictac.c * -------------- * This program plays a game of tic-tac-toe with the user. * The program is designed to emphasize the separation between * those aspects of the code that are common to all games and * those that are specific to tic-tac-toe. */#include <stdio.h>#include "stdgen.h"#include "stdstr.h"/* * Constants: WinningPosition, NeutralPosition, LosingPosition * ----------------------------------------------------------- * These constants define a rating system for game positions. * A rating is an integer centered at 0 as the neutral score: * ratings greater than 0 are good for the cuurent player, * ratings less than 0 are good for the opponent.  The * constants WinningPosition and LosingPosition are opposite * in value and indicate a position that is a forced win or * loss, respectively.  In a game in which the analysis is * complete, no intermediate values ever arise.  If the full * tree is too large to analyze, the EvaluatePosition function * returns integers that fall between the two extremes. */#define WinningPosition  1000#define NeutralPosition  0#define LosingPosition   (-WinningPosition)/* * Type: playerT * ------------- * This type is used to distinguish the human and computer * players and keep track of who has the current turn. */typedef enum { Human, Computer } playerT;/* * Type: moveT * ----------- * For any particular game, the moveT type must keep track of the * information necessary to describe a move.  For tic-tac-toe, * a moveT is simply an integer identifying the number of one of * the nine squares. */typedef int moveT;/* * Type: stateT * ------------ * For any game, the stateT structure records the current state * of the game.  For tic-tac-toe, the main component of the * state record is the board, which is an array of characters * using ‘X‘ for the first player, ‘O‘ for the second, and ‘ ‘ * for empty squares.  Although the board array is logically * a two-dimensional structure, it is stored as a linear array * so that its indices match the numbers used by the human * player to refer to the squares, as follows: * *        1 | 2 | 3 *       ---+---+--- *        4 | 5 | 6 *       ---+---+--- *        7 | 8 | 9 * * Note that element 0 is not used, which requires allocation * of an extra element. * * In addition to the board array, the code stores a playerT * value to indicate whose turn it is.  In this example, the * stateT structure also contains the total number of moves * so that functions can check this entry without counting * the number of occupied squares. */typedef struct {    char board[(3 * 3) + 1];    playerT whoseTurn;    int turnsTaken;} *stateT;/* * Constant: MaxMoves * ------------------ * This constant indicates the maximum number of legal moves * available on a turn and is used to allocate array space for * the legal move list.  This constant will change according * to the specifics of the game.  For tic-tac-toe, there are * never more than nine possible moves. */#define MaxMoves 9/* * Constant: MaxDepth * ------------------ * This constant indicates the maximum depth to which the * recursive search for the best move is allowed to proceed. * The use of a very large number for this constant ensures * that the analysis is carried out to the end of the game. */#define MaxDepth 10000/* * Constant: FirstPlayer * --------------------- * This constant indicates whether the human or the computer * player goes first and should be one of the enumerated * constants: Human or Computer. */#define FirstPlayer Computer/* * Private variable: winningLines * ------------------------------ * This two-dimensional array contains the index numbers of * the cells in each of the winning combinations.  Although * it is easy for the program to compute these values as it * runs, storing them in advance speeds up the execution. */static int winningLines[][3] = {    { 1, 2, 3 },    { 4, 5, 6 },    { 7, 8, 9 },    { 1, 4, 7 },    { 2, 5, 8 },    { 3, 6, 9 },    { 1, 5, 9 },    { 3, 5, 7 }};static int nWinningLines = sizeof winningLines                           / sizeof winningLines[0];/* Private function prototypes */static moveT FindBestMove(stateT state, int depth, int *pRating);static int EvaluatePosition(stateT state, int depth);static stateT NewGame(void);static void DisplayGame(stateT state);static void DisplayMove(moveT move);static void GiveInstructions(void);static char PlayerMark(playerT player);static moveT GetUserMove(stateT state);static bool MoveIsLegal(moveT move, stateT state);static moveT ChooseComputerMove(stateT state);static int GenerateMoveList(stateT state, moveT moveArray[]);static void MakeMove(stateT state, moveT move);static void RetractMove(stateT state, moveT move);static void AnnounceResult(stateT state);static bool GameIsOver(stateT state);static int EvaluateStaticPosition(stateT state);static bool CheckForWin(stateT state, playerT player);static playerT WhoseTurn(stateT state);static playerT Opponent(playerT player);/* * Main program * ------------ * The main program, along with the functions FindBestMove and * EvaluatePosition, are general in their design and can be * used with most two-player games.  The specific details of * tic-tac-toe do not appear in these functions and are instead * encapsulated in the stateT and moveT data structures and a * a variety of subsidiary functions. */main(){    stateT state;    moveT move;    GiveInstructions();    state = NewGame();    while (!GameIsOver(state)) {        DisplayGame(state);        switch (WhoseTurn(state)) {          case Human:            move = GetUserMove(state);            break;          case Computer:            move = ChooseComputerMove(state);            DisplayMove(move);            break;        }        MakeMove(state, move);    }    AnnounceResult(state);}/* * Function: FindBestMove * Usage: move = FindBestMove(state, depth, pRating); * -------------------------------------------------- * This function finds the best move for the current player, given * the specified state of the game.  The depth parameter and the * constant MaxDepth are used to limit the depth of the search * for games that are too difficult to analyze in full detail. * The function returns the best move and stores its rating in * the integer variable to which pRating points. */static moveT FindBestMove(stateT state, int depth, int *pRating){    moveT moveArray[MaxMoves], move, bestMove;    int i, nMoves, rating, minRating;    nMoves = GenerateMoveList(state, moveArray);    if (nMoves == 0) Error("No moves available");    minRating = WinningPosition + 1;    for (i = 0; i < nMoves && minRating != LosingPosition; i++) {        move = moveArray[i];        MakeMove(state, move);        rating = EvaluatePosition(state, depth + 1);        if (rating < minRating) {            bestMove = move;            minRating = rating;        }        RetractMove(state, move);    }    *pRating = -minRating;    return (bestMove);}/* * Function: EvaluatePosition * Usage: rating = EvaluatePosition(state, depth); * ----------------------------------------------- * This function evaluates a position by finding the rating of * the best move in that position.  The depth parameter and the * constant MaxDepth are used to limit the depth of the search. */static int EvaluatePosition(stateT state, int depth){    int rating;    if (GameIsOver(state) || depth >= MaxDepth) {        return (EvaluateStaticPosition(state));    }    (void) FindBestMove(state, depth, &rating);    return (rating);}/* * Function: NewGame * Usage: state = NewGame(); * ------------------------- * This function starts a new game and returns a stateT that * has been initialized to the defined starting configuration. */static stateT NewGame(void){    stateT state;    int i;    state = New(stateT);    for (i = 1; i <= 9; i++) {        state->board[i] = ‘ ‘;    }    state->whoseTurn = FirstPlayer;    state->turnsTaken = 0;    return (state);}/* * Function: DisplayGame * Usage: DisplayGame(state); * -------------------------- * This function displays the current state of the game. */static void DisplayGame(stateT state){    int row, col;    if (GameIsOver(state)) {        printf("\nThe final position looks like this:\n\n");    } else {        printf("\nThe game now looks like this:\n\n");    }    for (row = 0; row < 3; row++) {        if (row != 0) printf("---+---+---\n");        for (col = 0; col < 3; col++) {            if (col != 0) printf("|");            printf(" %c ", state->board[row * 3 + col + 1]);        }        printf("\n");    }    printf("\n");}/* * Function: DisplayMove * Usage: DisplayMove(move); * ------------------------- * This function displays the computer‘s move. */static void DisplayMove(moveT move){    printf("I‘ll move to square %d.\n", move);}/* * Function: GiveInstructions * Usage: GiveInstructions(); * -------------------------- * This function gives the player instructions about how to * play the game. */static void GiveInstructions(void){    printf("Welcome to tic-tac-toe.  The object of the game\n");    printf("is to line up three symbols in a row, vertically,\n");    printf("horizontally, or diagonally.  You‘ll be %c and\n",           PlayerMark(Human));    printf("I‘ll be %c.\n", PlayerMark(Computer));}/* * Function: PlayerMark * Usage: mark = PlayerMark(player); * --------------------------------- * This function returns the mark used on the board to indicate * the specified player.  By convention, the first player is * always X, so the mark used for each player depends on who * goes first. */static char PlayerMark(playerT player){    if (player == FirstPlayer) {        return (‘X‘);    } else {        return (‘O‘);    }}/* * Function: GetUserMove * Usage: move = GetUserMove(state); * --------------------------------- * This function allows the user to enter a move and returns the * number of the chosen square.  If the user specifies an illegal * move, this function gives the user the opportunity to enter * a legal one. */static moveT GetUserMove(stateT state){    moveT move;    printf("Your move.\n");    while (TRUE) {        printf("What square? ");        move = GetInteger();        if (MoveIsLegal(move, state)) break;        printf("That move is illegal.  Try again.\n");    }    return (move);}/* * Function: MoveIsLegal * Usage: if (MoveIsLegal(move, state)) . . . * ------------------------------------------ * This function returns TRUE if the specified move is legal * in the current state. */static bool MoveIsLegal(moveT move, stateT state){    return (move >= 1 && move <= 9 && state->board[move] == ‘ ‘);}/* * Function: ChooseComputerMove * Usage: move = ChooseComputerMove(state); * ---------------------------------------- * This function chooses the computer‘s move and is primarily * a wrapper for FindBestMove.  This function also makes it * possible to display any game-specific messages that need * to appear at the beginning of the computer‘s turn.  The * rating value returned by FindBestMove is simply discarded. */static moveT ChooseComputerMove(stateT state){    int rating;    printf("My move.\n");    return (FindBestMove(state, 0, &rating));}/* * Function: GenerateMoveList * Usage: n = GenerateMoveList(state, moveArray); * ---------------------------------------------- * This function generates a list of the legal moves available in * the specified state.  The list of moves is returned in the * array moveArray, which must be allocated by the client.  The * function returns the number of legal moves. */static int GenerateMoveList(stateT state, moveT moveArray[]){    int i, nMoves;    nMoves = 0;    for (i = 1; i <= 9; i++) {        if (state->board[i] == ‘ ‘) {            moveArray[nMoves++] = (moveT) i;        }    }    return (nMoves);}/* * Function: MakeMove * Usage: MakeMove(state, move); * ----------------------------- * This function changes the state of the game by making the * indicated move. */static void MakeMove(stateT state, moveT move){    state->board[move] = PlayerMark(state->whoseTurn);    state->whoseTurn = Opponent(state->whoseTurn);    state->turnsTaken++;}/* * Function: RetractMove * Usage: RetractMove(state, move); * -------------------------------- * This function changes the state of the game by "unmaking" the * indicated move. */static void RetractMove(stateT state, moveT move){    state->board[move] = ‘ ‘;    state->whoseTurn = Opponent(state->whoseTurn);    state->turnsTaken--;}/* * Function: AnnounceResult * Usage: AnnounceResult(state); * ----------------------------- * This function announces the result of the game. */static void AnnounceResult(stateT state){    DisplayGame(state);    if (CheckForWin(state, Human)) {        printf("You win\n");    } else if (CheckForWin(state, Computer)) {        printf("I win\n");    } else {        printf("Cat‘s game\n");    }}/* * Function: GameIsOver * Usage: if (GameIsOver(state)) . . . * ----------------------------------- * This function returns TRUE if the game is complete. */static bool GameIsOver(stateT state){    return (state->turnsTaken == 9            || CheckForWin(state, state->whoseTurn)            || CheckForWin(state, Opponent(state->whoseTurn)));}/* * Function: EvaluateStaticPosition * Usage: rating = EvaluateStaticPosition(state); * ---------------------------------------------- * This function gives the rating of a position without looking * ahead any further in the game tree.  Although this function * duplicates much of the computation of GameIsOver and therefore * introduces some runtime inefficiency, it makes the algorithm * somewhat easier to follow. */static int EvaluateStaticPosition(stateT state){    if (CheckForWin(state, state->whoseTurn)) {        return (WinningPosition);    }    if (CheckForWin(state, Opponent(state->whoseTurn))) {        return (LosingPosition);    }    return (NeutralPosition);}/* * Function: CheckForWin * Usage: if (CheckForWin(state, player)) . . . * -------------------------------------------- * This function returns TRUE if the specified player has won * the game.  The check on turnsTaken increases efficiency, * because neither player can win the game until the fifth move. */static bool CheckForWin(stateT state, playerT player){    int i;    char mark;    if (state->turnsTaken < 5) return (FALSE);    mark = PlayerMark(player);    for (i = 0; i < nWinningLines; i++) {        if (mark == state->board[winningLines[i][0]]               && mark == state->board[winningLines[i][1]]               && mark == state->board[winningLines[i][2]]) {            return (TRUE);        }    }    return (FALSE);}/* * Function: WhoseTurn * Usage: player = WhoseTurn(state); * --------------------------------- * This function returns whose turn it is, given the current * state of the game. */static playerT WhoseTurn(stateT state){    return (state->whoseTurn);}/* * Function: Opponent * Usage: opp = Opponent(player); * ------------------------------ * This function returns the playerT value corresponding to the * opponent of the specified player. */static playerT Opponent(playerT player){    switch (player) {      case Human:    return (Computer);      case Computer: return (Human);    }return 0;}
Test Results


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.