首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >具有简单AI的Tic Tac脚趾的Ncurse

具有简单AI的Tic Tac脚趾的Ncurse
EN

Code Review用户
提问于 2018-12-23 12:26:15
回答 3查看 1.3K关注 0票数 15

我已经读过你的规则,特别是关于咬一口大小的部分,所以我担心张贴这个,但无论如何都想做。我最近开始进入C语言,特别是为了历史鉴赏而制作一些游戏的愿望(至少是一个盗贼克隆人,可能更多)。Python是我最广泛使用的语言,在这一点上我还远不是专家。

如果有人有时间和欲望,这里有715行完整和工作的Tic脚趾游戏,我写在我的Ubuntu笔记本电脑上。人工智能不是天才,我可以让它变得更好,但这不是我在这里的重点.我关心的是编写结构合理的代码和可移植的、好看的ncurses显示器。为此,我想我做得还不错。我想听到的是我可以改进我的编码风格的所有方法,以便在制作未来游戏时更好地利用C语言和ncurses库。别退缩!每一件小事都能帮助我在不久的将来写出更好的游戏。我不需要任何人检查整个事情,除非他们真的想,简单地给我风格的提示是非常有帮助的。

代码语言:javascript
复制
// The mandatory tic tac toe game to practice ncurses stuff.
// Date Started: 21 DEC 2018

#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>


// Global Variables
time_t t;
int row, col, x, y, px, py, slen, sx;

// constants to use with the COLOR_PAIR() attribute for easier remembering
const int Xs = 1;
const int Os = 2;
const int BG = 3;

/*
    Board (7 x 7 character cells):
    Line 1 = ------- (7 hyphens)
    Line 2 = | | | | Spaces: 1=(1, 1) 2=(1, 3) 3=(1, 5) 
    Line 3 = -------         
    Line 4 = | | | |         4=(3, 1) 5=(3, 3) 6=(3, 5)  
    Line 5 = -------
    Line 6 = | | | |         7=(5, 1) 8=(5, 3) 9=(5, 5)
    Line 7 = -------
*/
const int line_len = 7;                       // constant int for the length of a board line

char break_lines[] = "-------";               // Strings representing the board lines, minus the pieces
char play_lines[] = "| | | |";

/*
    These spaces are abstractions, since the board itself will need to vary based on the current size of the terminal.
    They represent the current values of the "playable" spaces.
*/
char space_1 = ' ';
char space_2 = ' ';
char space_3 = ' ';
char space_4 = ' ';
char space_5 = ' ';
char space_6 = ' ';
char space_7 = ' ';
char space_8 = ' ';
char space_9 = ' ';
int space_1y, space_1x;
int space_2y, space_2x;
int space_3y, space_3x;
int space_4y, space_4x;
int space_5y, space_5x;
int space_6y, space_6x;
int space_7y, space_7x;
int space_8y, space_8x;
int space_9y, space_9x;

char *space_ptr = NULL;                       // Pointer to one of the playable spaces

// Player's side ('X' or 'O')
char side;                    

int running = 1;                              // Main menu control variable
int playing = 1;                              // Game loop control variable
int turn = 1;                                 // Turn number
int game_over = 0;

// Function Declarations
void f_intro();                               // Print elaborate "animated" intro splash
char f_setup();                               // Prompt player to pick a side or pick random selection, returns char
void f_paint();                               // Paint the board state on a given turn
void f_turn_input();                          // Take player input and determine AI action (includes sub-functions probably)
void f_player_turn();                         // Take player input and place the appropriate mark on the board
void f_AI_turn();                             // Logic (such as it is) and Placement for AI turn 
void f_evaluate_turn();                       // Check for endgame state and either advance the turn or end the game (with sub-functions probably)
int f_AI_picker();                            // Picks random spots until it finds an empty one or tries them all
void f_declare_winner(char symbol);           // Takes the winning character and creates a splash screen declaring victory ('X', 'O', or 'T' for Tie)


// Main Function
int main(){
    srand((unsigned) time(&t));
    initscr();
    clear();
    cbreak();
    keypad(stdscr, 1);
    curs_set(0);
    noecho();
    start_color();
    init_pair(Xs, COLOR_CYAN, COLOR_BLACK);
    init_pair(Os, COLOR_RED, COLOR_BLACK);
    init_pair(BG, COLOR_YELLOW, COLOR_BLACK);

    f_intro();                      // Print intro splash
    getch();

    while(running){
        clear();
        side = f_setup();             // Choose X's, O's, or RANDOM SIDE
        playing = 1;
        while(playing){
            f_paint();                  // Paint the board as it is that turn
            f_turn_input();             // Take player input and if valid determine AI move + sub-functions
            turn++;
        }
        // To-Do, a reset function
    }

    endwin();
    return 0;
}

// Function Definitions
void f_intro(){                   
    // Print elaborate "animated" intro splash
    int which;
    clear();
    getmaxyx(stdscr, row, col);
    // Print the background
    for(y=0;y<=row;y++){
        for(x=0;x<=col;x++){
            which = rand() % 3;
            if(which == 0){
                // Print an "X" in the cell
                attron(COLOR_PAIR(Xs));
                mvprintw(y, x, "X");
                attroff(COLOR_PAIR(Xs));
            }else if(which == 1){
                // Print an "O" in the cell
                attron(COLOR_PAIR(Os));
                mvprintw(y, x, "O");
                attroff(COLOR_PAIR(Os));
            }else if(which == 2){
                // Print a blank black space in the cell
                attron(COLOR_PAIR(BG));
                mvprintw(y, x, " ");
                attroff(COLOR_PAIR(BG));
            }
        }
    }
    // Print the Title
    y = row / 2 - 1;
    char intro_str[] = " NCURSES Tic Tac Toe! ";
    char intro_str_padding[] = "                      "; 
    char intro_str2[] = " any key to continue  "; 
    slen = strlen(intro_str);
    x = col / 2 - slen / 2;
    mvprintw(y++, x, intro_str_padding);
    mvprintw(y++, x, intro_str);
    mvprintw(y++, x, intro_str2);
    mvprintw(y, x, intro_str_padding);

    refresh();
}

char f_setup(){                   
    // Prompt player to pick a side or pick random selection, returns char
    int input;
    clear();
    getmaxyx(stdscr, row, col);
    char setup_str1[] = "Pick a side!";
    char setup_str2[] = "Press 'X', 'O', or 'R' for Random!";
    char *chose_x = "You chose X's! Any key to continue...";
    char *chose_y = "You chose O's! Any key to continue...";
    char *choice_ptr = NULL;
    y = row / 2 - 1;
    slen = strlen(setup_str1);
    x = col / 2 - slen / 2;
    mvprintw(y++, x, setup_str1);
    slen = strlen(setup_str2);
    x = col / 2 - slen / 2;
    mvprintw(y++, x, setup_str2);
    y++;
    refresh();
    input = getch();
    if(input == 'X' || input == 'x'){
        choice_ptr = chose_x;
        slen = strlen(choice_ptr);
        x = col / 2 - slen / 2;
        mvprintw(y, x, choice_ptr);
        refresh();
        getch();
        return 'X';
    }else if(input == 'O' || input == 'o'){
        choice_ptr = chose_y;
        slen = strlen(choice_ptr);
        x = col / 2 - slen / 2;
        mvprintw(y, x, choice_ptr);
        refresh();
        getch();
        return 'O';
    }else if(input == 'R' || input == 'r'){
        int r;
        r = rand() % 2;
        if(r == 0){
            // Pick 'X'
            choice_ptr = chose_x;
            slen = strlen(choice_ptr);
            x = col / 2 - slen / 2;
            mvprintw(y, x, choice_ptr);
            refresh();
            getch();
            return 'X';
        }else if(r == 1){
            // Pick 'O'
            choice_ptr = chose_y;
            slen = strlen(choice_ptr);
            x = col / 2 - slen / 2;
            mvprintw(y, x, choice_ptr);
            refresh();
            getch();
            return 'O';
        }
    }else{
        char err_str[] = "Input error! Any key to continue...";
        slen = strlen(err_str);
        x = col / 2 - slen / 2;
        mvprintw(y, x, err_str);
        refresh();
        getch();
        f_setup();
    }
}

void f_paint(){                   
    // Paint the board state on a given turn
    /*
        1. Clear screen.
        2. Paint blank board.
        3. Paint the contents of each playable cell.
        4. Refresh screen
    */
    clear();                                                    // Clear screen
    getmaxyx(stdscr, row, col);                                 // Get current size of terminal
    y = row / 2 - 3;                                            // Board is 7x7 characters, so (y / 2 - 3) is a decent top edge
    x = col / 2 - 3;                                            // Ditto for (x / 2 - 3) being a decent left edge.
    // Determine the locations of the 9 "playable" cells:
    space_1y = y + 1; space_1x = x + 1;
    space_2y = y + 1; space_2x = x + 3;
    space_3y = y + 1; space_3x = x + 5;
    space_4y = y + 3; space_4x = x + 1;
    space_5y = y + 3; space_5x = x + 3;
    space_6y = y + 3; space_6x = x + 5;
    space_7y = y + 5; space_7x = x + 1;
    space_8y = y + 5; space_8x = x + 3;
    space_9y = y + 5; space_9x = x + 5;
    // Paint the board roughly centered:
    int yy, xx;
    attron(COLOR_PAIR(BG));
    for(yy = 0; yy < line_len; yy++){
        if(yy == 0 || yy % 2 == 0){
            mvprintw(y + yy, x, break_lines);
        }else{
            mvprintw(y + yy, x, play_lines);
        }
    }
    attroff(COLOR_PAIR(BG));
    // Insert appropriate characters into the "playable" cells:
    if(space_1 == 'X'){
        attron(COLOR_PAIR(Xs));
    }else if(space_1 == 'O'){
        attron(COLOR_PAIR(Os));
    }
    mvaddch(space_1y, space_1x, space_1);
    if(space_2 == 'X'){
        attron(COLOR_PAIR(Xs));
    }else if(space_2 == 'O'){
        attron(COLOR_PAIR(Os));
    }
    mvaddch(space_2y, space_2x, space_2);
    if(space_3 == 'X'){
        attron(COLOR_PAIR(Xs));
    }else if(space_3 == 'O'){
        attron(COLOR_PAIR(Os));
    }
    mvaddch(space_3y, space_3x, space_3);
    if(space_4 == 'X'){
        attron(COLOR_PAIR(Xs));
    }else if(space_4 == 'O'){
        attron(COLOR_PAIR(Os));
    }
    mvaddch(space_4y, space_4x, space_4);
    if(space_5 == 'X'){
        attron(COLOR_PAIR(Xs));
    }else if(space_5 == 'O'){
        attron(COLOR_PAIR(Os));
    }
    mvaddch(space_5y, space_5x, space_5);
    if(space_6 == 'X'){
        attron(COLOR_PAIR(Xs));
    }else if(space_6 == 'O'){
        attron(COLOR_PAIR(Os));
    }
    mvaddch(space_6y, space_6x, space_6);
    if(space_7 == 'X'){
        attron(COLOR_PAIR(Xs));
    }else if(space_7 == 'O'){
        attron(COLOR_PAIR(Os));
    }
    mvaddch(space_7y, space_7x, space_7);
    if(space_8 == 'X'){
        attron(COLOR_PAIR(Xs));
    }else if(space_8 == 'O'){
        attron(COLOR_PAIR(Os));
    }
    mvaddch(space_8y, space_8x, space_8);
    if(space_9 == 'X'){
        attron(COLOR_PAIR(Xs));
    }else if(space_9 == 'O'){
        attron(COLOR_PAIR(Os));
    }
    mvaddch(space_9y, space_9x, space_9);
    attroff(COLOR_PAIR(Xs));
    attroff(COLOR_PAIR(Os));
    refresh();
}

void f_turn_input(){              
    // Take player input and determine AI action (includes sub-functions probably)
    /*
        1. Determine who goes first. 
            - Using if/else to divide the function into two halves for each possibility.
        2. Player/AI Takes turn. -> Refresh
        3. Player/AI takes turn. -> Refresh

        Note on AI: No real logic for this version. Just going to randomly pick from the available spaces. 
    */
    if(side == 'X'){
        // if player is 'X':
        f_player_turn();
        f_evaluate_turn();
        if(game_over == 0){
            f_AI_turn();
            f_evaluate_turn();
        }
    }else if(side == 'O'){
        // If player is 'O':
        f_AI_turn();
        f_evaluate_turn();
        if(game_over == 0){
            f_player_turn();
            f_evaluate_turn();
        }
    }
    refresh();
}


void f_player_turn(){             
    // Take player input and place the appropriate mark on the board
    int info_line = y + 10;                                // Determine the line that the info splash will show up on.
    char move_splash[] = "Use arrow keys and press 'P' to place your piece!";
    char done_splash[] = "Good move!";
    char move_err_splash[] = "You can't move that way!";
    char input_err_splash[] = "Invalid input!";
    char full_err_splash[] = "Spot already claimed!";
    slen = strlen(move_splash);
    sx = col / 2 - slen / 2;                               // Center the info splash
    mvprintw(info_line, sx, move_splash);
    curs_set(1);                                           // Enable the cursor for the player
    int pos_y = space_1y;                                  // Y position of the cursor
    int pos_x = space_1x;                                  // X position of the cursor
    move(pos_y, pos_x);                                    // Move it to space 1
    refresh();
    int inputting = 1;
    while(inputting){
        int input;
        char spot;
        int cx;
        input = getch();
        if(input == KEY_LEFT){
            if(!(pos_x == space_1x)){                          
                // If not on the left playable edge
                pos_x -= 2;
                move(pos_y, pos_x);
            }else{
                for(cx = sx; cx <= col; cx++){
                    // Clear the info line
                    mvaddch(info_line, cx, ' ');
                }
                slen = strlen(move_err_splash);
                sx = col / 2 - slen / 2;
                mvprintw(info_line, sx, move_err_splash);
                move(pos_y, pos_x);
            }
        }else if(input == KEY_RIGHT){
            if(!(pos_x == space_3x)){                          
                // If not on the right playable edge
                pos_x += 2;
                move(pos_y, pos_x);
            }else{
                for(cx = sx; cx <= col; cx++){
                    // Clear the info line
                    mvaddch(info_line, cx, ' ');
                }
                slen = strlen(move_err_splash);
                sx = col / 2 - slen / 2;
                mvprintw(info_line, sx, move_err_splash);   
                move(pos_y, pos_x);
            }
        }else if(input == KEY_UP){
            if(!(pos_y == space_1y)){                          
                // If not on the top playable edge
                pos_y -= 2;
                move(pos_y, pos_x);
            }else{
                for(cx = sx; cx <= col; cx++){
                    // Clear the info line
                    mvaddch(info_line, cx, ' ');
                }
                slen = strlen(move_err_splash);
                sx = col / 2 - slen / 2;
                mvprintw(info_line, sx, move_err_splash);   
                move(pos_y, pos_x);                                           
            }
        }else if(input == KEY_DOWN){
            if(!(pos_y == space_9y)){                          
                // If not on the bottom playable edge
                pos_y += 2;
                move(pos_y, pos_x);
            }else{
                for(cx = sx; cx <= col; cx++){
                    // Clear the info line
                    mvaddch(info_line, cx, ' ');
                }
                slen = strlen(move_err_splash);
                sx = col / 2 - slen / 2;
                mvprintw(info_line, sx, move_err_splash);   
                move(pos_y, pos_x);
            }
        }else if(input == 'P' || input == 'p'){
            /*
                1. Read contents of space.
                2. If Empty -> Place player's symbol
                3. Else, try again
            */
            if(pos_y == space_1y && pos_x == space_1x){
                space_ptr = &space_1;   
            }else if(pos_y == space_2y && pos_x == space_2x){
                space_ptr = &space_2;
            }else if(pos_y == space_3y && pos_x == space_3x){
                space_ptr = &space_3;
            }else if(pos_y == space_4y && pos_x == space_4x){
                space_ptr = &space_4;
            }else if(pos_y == space_5y && pos_x == space_5x){
                space_ptr = &space_5;
            }else if(pos_y == space_6y && pos_x == space_6x){
                space_ptr = &space_6;
            }else if(pos_y == space_7y && pos_x == space_7x){
                space_ptr = &space_7;
            }else if(pos_y == space_8y && pos_x == space_8x){
                space_ptr = &space_8;
            }else if(pos_y == space_9y && pos_x == space_9x){
                space_ptr = &space_9;
            }
            if(*space_ptr == ' '){
                if(side == 'X'){
                    *space_ptr = 'X';
                }else{
                    *space_ptr = 'O';
                }
                for(cx = sx; cx <= col; cx++){
                    // Clear the info line
                    mvaddch(info_line, cx, ' ');
                }
                slen = strlen(done_splash);
                sx = col / 2 - slen / 2;
                mvprintw(info_line, sx, done_splash);   
                move(pos_y, pos_x);
                refresh();
                inputting = 0;
            }else{
                for(cx = sx; cx <= col; cx++){
                    // Clear the info line
                    mvaddch(info_line, cx, ' ');
                }
                slen = strlen(full_err_splash);
                sx = col / 2 - slen / 2;
                mvprintw(info_line, sx, full_err_splash);   
                move(pos_y, pos_x);
            }
        }else{
            // If the user presses any other button 
            for(cx = sx; cx <= col; cx++){
                // Clear the info line
                mvaddch(info_line, cx, ' ');
            }
            slen = strlen(input_err_splash);
            sx = col / 2 - slen / 2;
            mvprintw(info_line, sx, input_err_splash);
            move(pos_y, pos_x);
        }
    }
}

int f_AI_picker(){
    /*
        1. Pick a number between 1 and 9
        2. Randomly decide whether to check spaces from 1 to 9 or 9 to 1 for the sake of variety
        3. Check them in the determined order until an open space is found.
        4. Return number of open space.

        Note: This version has no real strategic logic and will be easily beaten by any player.
              Although a quick fix for some added challenge is to make it prioritize the center tile.
    */
    int pick;
    pick = rand() % 9 + 1;
    int order;                                      // 1 = Ascending, 2 = Descending
    order = rand() % 2 + 1;
    if(space_5 == ' '){
        return 5;
    }else{
        if(order == 1){
            if(space_1 == ' '){
                return 1;
            }else if(space_2 == ' '){
                return 2;
            }else if(space_3 == ' '){
                return 3;
            }else if(space_4 == ' '){
                return 4;
            }else if(space_6 == ' '){
                return 6;
            }else if(space_7 == ' '){
                return 7;
            }else if(space_8 == ' '){
                return 8;
            }else if(space_9 == ' '){
                return 9;
            }
        }else if(order == 2){
            if(space_9 == ' '){
                return 9;
            }else if(space_8 == ' '){
                return 8;
            }else if(space_7 == ' '){
                return 7;
            }else if(space_6 == ' '){
                return 6;
            }else if(space_4 == ' '){
                return 4;
            }else if(space_3 == ' '){
                return 3;
            }else if(space_2 == ' '){
                return 2;
            }else if(space_1 == ' '){
                return 1;
            }
        }
    }
}

void f_AI_turn(){                 
    // Logic (such as it is) and Placement for AI turn 
    char AI_char;
    if(side == 'X'){
        AI_char = 'O';
    }else{
        AI_char = 'X';
    }
    int space_to_place;
    space_to_place = f_AI_picker();
    if(space_to_place == 1){
        space_1 = AI_char;
    }else if(space_to_place == 2){
        space_2 = AI_char;
    }else if(space_to_place == 3){
        space_3 = AI_char;
    }else if(space_to_place == 4){
        space_4 = AI_char;
    }else if(space_to_place == 5){
        space_5 = AI_char;
    }else if(space_to_place == 6){
        space_6 = AI_char;
    }else if(space_to_place == 7){
        space_7 = AI_char;
    }else if(space_to_place == 8){
        space_8 = AI_char;
    }else if(space_to_place == 9){
        space_9 = AI_char;
    }
    f_paint();
    refresh();  
}

void f_declare_winner(char symbol){           
    // Takes the winning character and creates a splash screen declaring victory ('X', 'O', or 'T' for Tie)
    char *x_wins = "  X is the winner!  ";
    char *o_wins = "  O is the winner!  ";
    char *tie_game = " The game is a tie! ";
    char padding[] = "                   ";
    char *win_splash_ptr = NULL;
    // Paint background for victory splash:
    if(symbol == 'X'){
      win_splash_ptr = x_wins;
      attron(COLOR_PAIR(Xs));
        for(y = 0; y <= row; y++){
            for(x = 0; x <= col; x++){
                if(x == 0 || x % 2 == 0){
                    mvaddch(y, x, 'X'); 
                }else{
                    mvaddch(y, x, ' ');
                }
            }
        }
      attroff(COLOR_PAIR(Xs));
    }else if(symbol == 'O'){
        win_splash_ptr = o_wins;
      attron(COLOR_PAIR(Os));
        for(y = 0; y <= row; y++){
            for(x = 0; x <= col; x++){
                if(x == 0 || x % 2 == 0){
                    mvaddch(y, x, 'O'); 
                }else{
                mvaddch(y, x, ' ');
                }
            }
        }
      attroff(COLOR_PAIR(Os));
    }else if(symbol == 'T'){
        win_splash_ptr = tie_game;
        for(y = 0; y <= row; y++){
            for(x = 0; x <= col; x++){
                if(x == 0 || x % 2 == 0){
                    attron(COLOR_PAIR(Xs));
                    mvaddch(y, x, 'X'); 
                    attroff(COLOR_PAIR(Xs));
                }else{
                    attron(COLOR_PAIR(Os));
                    mvaddch(y, x, 'O');
                    attroff(COLOR_PAIR(Os));
                }
            }
        }
    }
    //Paint the prompt
    y = row / 2 - 2;
    slen = strlen(win_splash_ptr);
    x = col / 2 - slen / 2;
    mvprintw(y++, x, padding);
    mvprintw(y++, x, win_splash_ptr);
    mvprintw(y, x, padding);
    curs_set(0);
    refresh();
    getch();
    running = 0;
    playing = 0;
}

void f_evaluate_turn(){           
    // Check for endgame state and either advance the turn or end the game (with sub-functions probably)
    /*
        1. Check for each possible victory condition. -> If so, declare appropriate victory.
        2. Check if turn number is high enough to indicate a tie -> If so, declare a tie.
        3. Else, continue.
    */
    int winner;
    winner = 'N';  // For none
    if(space_1 == 'O' && space_2 == 'O' && space_3 == 'O'){
        winner = 'O';
        game_over++;
    }else if(space_4 == 'O' && space_5 == 'O' && space_6 == 'O'){
        winner = 'O';
        game_over++;
    }else if(space_7 == 'O' && space_8 == 'O' && space_9 == 'O'){
        winner = 'O';
        game_over++;
    }else if(space_1 == 'O' && space_4 == 'O' && space_7 == 'O'){
        winner = 'O';
        game_over++;
    }else if(space_2 == 'O' && space_5 == 'O' && space_8 == 'O'){
        winner = 'O';
        game_over++;
    }else if(space_3 == 'O' && space_6 == 'O' && space_9 == 'O'){
        winner = 'O';
        game_over++;
    }else if(space_1 == 'O' && space_5 == 'O' && space_9 == 'O'){
        winner = 'O';
        game_over++;
    }else if(space_3 == 'O' && space_5 == 'O' && space_7 == 'O'){
        winner = 'O';
        game_over++;
    }else if(space_1 == 'X' && space_2 == 'X' && space_3 == 'X'){
        winner = 'X';
        game_over++;
    }else if(space_4 == 'X' && space_5 == 'X' && space_6 == 'X'){
        winner = 'X';
        game_over++;
    }else if(space_7 == 'X' && space_8 == 'X' && space_9 == 'X'){
        winner = 'X';
        game_over++;
    }else if(space_1 == 'X' && space_4 == 'X' && space_7 == 'X'){
        winner = 'X';
        game_over++;
    }else if(space_2 == 'X' && space_5 == 'X' && space_8 == 'X'){
        winner = 'X';
        game_over++;
    }else if(space_3 == 'X' && space_6 == 'X' && space_9 == 'X'){
        winner = 'X';
        game_over++;
    }else if(space_1 == 'X' && space_5 == 'X' && space_9 == 'X'){
        winner = 'X';
        game_over++;
    }else if(space_3 == 'X' && space_5 == 'X' && space_7 == 'X'){
        winner = 'X';
        game_over++;
    }else if(turn >= 5){
        winner = 'T';
        game_over++;
    }
    if(winner != 'N'){
        f_declare_winner(winner);   
    }
}

当然,这是不言而喻的,但任何其他linux用户,谁想把它扔到他们的主目录,并自己播放它,是非常欢迎这样做!只是要知道,没有更多的战略思考,人工智能是一个完全的推手。我所做的只是把中心广场的优先级放在首位。

EN

回答 3

Code Review用户

回答已采纳

发布于 2018-12-23 16:04:23

避免编号变量

char space_1 =‘';char space_2 = ';char space_3 =’';char space_4 =‘';char space_5 =’';char space_6 =‘';char space_7 =’';char space_8 =‘';char space_9 =’';int space_1y,space_1x;int space_2y,space_2x;int space_3y,space_3x;int space_4y,space_4x;int space_3y,space_4x。int space_6y,space_6x;int space_7y,space_7x;int space_8y,space_8x;int space_9y,space_9x;

如果您发现自己使用了编号变量,那么您几乎总是应该使用数组。例如。

代码语言:javascript
复制
const int CELL_COUNT = 9;

typedef struct {
    int y;
    int x;
} Location;

char cells[CELL_COUNT] = "         ";
Location cell_locations[CELL_COUNT];

即使使用常量和类型声明,这仍然比原来的短。现在您可以参考,例如cell_locations[0].x,它更多的是自文档化。我们有一个可执行的类型模式,而不是命名约定。Location必须有一个y和一个x。必须有CELL_COUNT (9) cell_locations

您可以将Location移动到可以重用的头文件中。

后来,而不是

space\_1y = y + 1; space\_1x = x + 1; space\_2y = y + 1; space\_2x = x + 3; space\_3y = y + 1; space\_3x = x + 5; space\_4y = y + 3; space\_4x = x + 1; space\_5y = y + 3; space\_5x = x + 3; space\_6y = y + 3; space\_6x = x + 5; space\_7y = y + 5; space\_7x = x + 1; space\_8y = y + 5; space\_8x = x + 3; space\_9y = y + 5; space\_9x = x + 5;

我们可以这样说

代码语言:javascript
复制
    int i = 0;
    for (int current_y = y + 1, n = y + line_len; current_y < n; current_y += 2) {
        for (int current_x = x + 1, m = x + line_len; current_x < m; x += 2) {
            cell_locations[i].y = current_y;
            cell_locations[i].x = current_x;
            i++;
        }
    }

而不是九个

if(space\_1 == 'X'){ attron(COLOR\_PAIR(Xs)); }else if(space\_1 == 'O'){ attron(COLOR\_PAIR(Os)); } mvaddch(space\_1y, space\_1x, space\_1);

我们可以这样说

代码语言:javascript
复制
int determine_color_pair(char mark) {
    if (mark == 'X') {
        return Xs;
    }

    if (mark == 'O') {
        return Os;
    }

    return BG;
} 

代码语言:javascript
复制
    for (int j = 0; j < CELL_COUNT; j++) {
         attron(COLOR_PAIR(determine_color_pair(cells[j])));
         mvaddch(cell_locations[j].y, cell_locations[j].x, cells[j]);
    }

更喜欢描述性名称而不是注释

void f_intro();//打印精心制作的“动画”介绍

有了正确的命名,您就不需要对这样的事情发表评论了。

代码语言:javascript
复制
void display_intro();

甚至是

代码语言:javascript
复制
void display_elaborate_pseudo_animated_intro_splash();

虽然我觉得这太过分了。但是对于一个只调用一次的函数,这种名称是可能的。

我不喜欢使用f_前缀来表示函数。函数一般应该有动词名,因为它们可以做事情。变量有名词名,因为它们代表事物。更常见的做法是使用前缀将代码与其他可能链接的代码(如ttt_display_intro )分开。然后,如果您将您的Tic-Tac-Toe游戏与基于文本的RPG (例如Zork或Rogue)链接起来,那么您可以在这两种游戏中都具有display_intro函数。

在代码示例中,它们经常对每个变量进行注释,以达到教学目的。我觉得这很不幸,因为它使人们对如何使用评论有了不切实际的看法。注释应该用来解释为什么代码会做一些事情,而不是代码正在做什么。代码本身应该足以告诉人们代码正在做什么。

我觉得代码后面的注释在同一行上的做法令人讨厌。我宁愿看到

代码语言:javascript
复制
// Takes the winning character and creates a splash screen declaring victory ('X', 'O', or 'T' for Tie)
void f_declare_winner(char symbol);

注意如何去掉滚动条。

如果注释不需要出现在左边的列中,那么没有它可能会更好。

Simplification

}else if(input == 'R' || input == 'r'){ int r; r = rand() % 2; if(r == 0){ // Pick 'X' choice\_ptr = chose\_x; slen = strlen(choice\_ptr); x = col / 2 - slen / 2; mvprintw(y, x, choice\_ptr); refresh(); getch(); return 'X'; }else if(r == 1){ // Pick 'O' choice\_ptr = chose\_y; slen = strlen(choice\_ptr); x = col / 2 - slen / 2; mvprintw(y, x, choice\_ptr); refresh(); getch(); return 'O'; }

整个街区都可以被移除。

替换

input = getch(); if(input == 'X' || input == 'x'){

使用

代码语言:javascript
复制
    input = toupper(getch());
    if (input == 'R') {
        int r = rand() % 2;
        input = (r == 0) ? 'X' : 'O';
    }

    if (input == 'X') {

现在,您不必重复代码。

在功能结束时,

f\_setup();

很可能是

代码语言:javascript
复制
        return f_setup();

或者将事情更改为使用无限循环而不是递归调用。

代码语言:javascript
复制
void display_prompt_and_wait_for_input(const char *choice_ptr) {
    int slen = strlen(choice_ptr);
    x = col / 2 - slen / 2;
    mvprintw(y, x, choice_ptr);
    refresh();
    getch();
}

// Prompt player to pick a side or pick random selection, returns char
char f_setup() {
    const char setup_str1[] = "Pick a side!";
    const char setup_str2[] = "Press 'X', 'O', or 'R' for Random!";
    char *chose_x = "You chose X's! Any key to continue...";
    char *chose_y = "You chose O's! Any key to continue...";

    for (;;) {
        clear();
        getmaxyx(stdscr, row, col);
        y = row / 2 - 1;
        int slen = strlen(setup_str1);
        x = col / 2 - slen / 2;
        mvprintw(y++, x, setup_str1);
        slen = strlen(setup_str2);
        x = col / 2 - slen / 2;
        mvprintw(y++, x, setup_str2);
        y++;
        refresh();

        int input = toupper(getch());
        if (input == 'R') {
            int r = rand() % 2;
            input = (r == 0) ? 'X' : 'O';
        }

        switch (input) {
            case 'X':
                display_prompt_and_wait_for_input(chose_x);
                return 'X';
            case 'O':
                display_prompt_and_wait_for_input(chose_y);
                return 'O';
            default:
                char *err_str = "Input error! Any key to continue...";
                display_prompt_and_wait_for_input(err_str);
        }
    }
}
票数 13
EN

Code Review用户

发布于 2018-12-23 16:33:04

以下是一些可以帮助您改进程序的事情。

消除了实际

中的全局变量

拥有依赖于全局变量的例程使得理解逻辑变得更加困难,并引入了许多错误的机会。对于这个程序来说,将几乎所有的全局变量封装在一个struct中是很简单和自然的,这样就可以清楚地知道哪些事情是一起进行的。然后,您只需要传递一个指向该struct的指针。在某些情况下,例如x,可以简单地将它作为局部变量放入使用该变量的函数中。更好的是,在t的情况下,可以完全消除它,因为time(NULL)可以满足您的需要。

消除未使用的变量

这段代码声明变量pxpy,但对它们不做任何操作。如果您知道如何让编译器找到这类问题,那么编译器就足够聪明了。

使用更多的空格来增强代码

的可读性

而不是像这样把事情挤在一起:

代码语言:javascript
复制
for(y=0;y<=row;y++){
    for(x=0;x<=col;x++){

如果你使用更多的空间,大多数人会发现它更容易读懂:

代码语言:javascript
复制
for(y = 0; y <= row; y++) {
    for(x = 0;x <= col; x++) {

不要重复你自己(干)

其中有很多重复的代码,还有一些特殊的变量,比如通过space_1通过space_9实现的。只要简单地使用数组space[9]并使用索引变量来遍历这些空间,就可以大大改善这一点。对于过长的f_evaluate_turn()函数,也可以做类似的事情。

从函数

返回有用的东西

函数当前的编写方式,大多数返回void,但这妨碍了代码的简化。例如,而不是这样:

代码语言:javascript
复制
f_player_turn();
f_evaluate_turn();
if(game_over == 0){
    f_AI_turn();
    f_evaluate_turn();
}

如果每个回合评估自己并返回true (如果游戏尚未结束),您可以这样写:

代码语言:javascript
复制
if (f_player_turn()) {
    f_AI_turn();
}

使用更好的命名

有些名称(如game_overrunning )是好的,因为它们是描述性的,但其他名称(如sx )对它们的含义没有给出多少提示。而且,对所有函数使用f_前缀是很烦人的,而且会使代码混乱。

使用更好的随机数生成器

您目前正在使用

代码语言:javascript
复制
which = rand() % 3;

这种方法存在一些问题。这会产生比更高的数字更低的数字--这不是一个均匀的分布。另一个问题是随机数发生器的低阶位不是特别随机的,所以结果也不是特别随机。在我的机器上,有一个轻微的,但可测量的偏差,向0。有关详细信息,请参阅这个答案,但我建议将其更改为类似于链接中的内容。在这个特定的应用程序中,这并不重要,但总体上了解是很好的。

创建和使用较小的函数

如果使用较小的函数,这段代码可以更短、更容易阅读、理解和修改。例如,始终如一地使用这样的函数将大大提高代码的可读性:

代码语言:javascript
复制
void place(int x, int y, char ch) {
    switch (ch) {
        case ' ':
            attron(COLOR_PAIR(BG));
            mvprintw(y, x, " ");
            attroff(COLOR_PAIR(BG));
            break;
        case 'X':
            attron(COLOR_PAIR(Xs));
            mvprintw(y, x, "X");
            attroff(COLOR_PAIR(Xs));
            break;
        case 'O':
            attron(COLOR_PAIR(Os));
            mvprintw(y, x, "O");
            attroff(COLOR_PAIR(Os));
            break;
    }
}
票数 12
EN

Code Review用户

发布于 2018-12-23 15:07:59

这些:

代码语言:javascript
复制
char space_1 = ' ';
char space_2 = ' ';
char space_3 = ' ';
char space_4 = ' ';
char space_5 = ' ';
char space_6 = ' ';
char space_7 = ' ';
char space_8 = ' ';
char space_9 = ' ';
int space_1y, space_1x;
int space_2y, space_2x;
int space_3y, space_3x;
int space_4y, space_4x;
int space_5y, space_5x;
int space_6y, space_6x;
int space_7y, space_7x;
int space_8y, space_8x;
int space_9y, space_9x;

应该被重构成三个数组。这将允许您编写正常的循环并减少代码中的重复。

对于所有全局变量,以及除main之外的所有函数,都应该声明它们为static,因为它们不会导出到任何其他模块。

您的runningplaying变量实际上是布尔值,因此您应该使用<stdbool.h>

xy作为全局值似乎是不明智的,特别是在循环中使用它们时:

代码语言:javascript
复制
for(y=0;y<=row;y++){
    for(x=0;x<=col;x++){

它们可能应该保持为局部变量,在这种情况下,应该在循环声明中实例化它们。

您的if(which == 0){可以由一个开关代替,因为您要对它进行三次比较。

char *chose_x和任何其他不改变的字符串都应该声明为const

这是:

代码语言:javascript
复制
if(input == 'O' || input == 'o')

应:

代码语言:javascript
复制
if (tolower(input) == 'o')

类似的情况也是如此。

这是:

代码语言:javascript
复制
x = col / 2 - slen / 2;

可以是:

代码语言:javascript
复制
x = (col - slen) / 2;

不过,正如@PeterCordes正确指出的那样,如果使用有符号整数,则通常需要小心应用此规则。是的,如果您知道数据不会是负的,那么最好将变量设置为unsigned

这是:

代码语言:javascript
复制
    if(yy == 0 || yy % 2 == 0){
        mvprintw(y + yy, x, break_lines);
    }else{
        mvprintw(y + yy, x, play_lines);
    }

为了简单起见,应该使用中间变量,第一个== 0是冗余的:

代码语言:javascript
复制
char *lines;
if (!(yy % 2))
    lines = break_lines;
else
    lines = play_lines;
mvprintw(y + yy, x, lines);

Print an "X" in the cell代码中执行类似的临时变量策略。这是以DRY的名义(“不要重复自己”)。

因为您不会修改这个:

代码语言:javascript
复制
char done_splash[] = "Good move!";

相反,您应该将其声明为const char*而不是数组。

票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/210217

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档