我已经读过你的规则,特别是关于咬一口大小的部分,所以我担心张贴这个,但无论如何都想做。我最近开始进入C语言,特别是为了历史鉴赏而制作一些游戏的愿望(至少是一个盗贼克隆人,可能更多)。Python是我最广泛使用的语言,在这一点上我还远不是专家。
如果有人有时间和欲望,这里有715行完整和工作的Tic脚趾游戏,我写在我的Ubuntu笔记本电脑上。人工智能不是天才,我可以让它变得更好,但这不是我在这里的重点.我关心的是编写结构合理的代码和可移植的、好看的ncurses显示器。为此,我想我做得还不错。我想听到的是我可以改进我的编码风格的所有方法,以便在制作未来游戏时更好地利用C语言和ncurses库。别退缩!每一件小事都能帮助我在不久的将来写出更好的游戏。我不需要任何人检查整个事情,除非他们真的想,简单地给我风格的提示是非常有帮助的。
// 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用户,谁想把它扔到他们的主目录,并自己播放它,是非常欢迎这样做!只是要知道,没有更多的战略思考,人工智能是一个完全的推手。我所做的只是把中心广场的优先级放在首位。
发布于 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;
如果您发现自己使用了编号变量,那么您几乎总是应该使用数组。例如。
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;
我们可以这样说
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);
我们可以这样说
int determine_color_pair(char mark) {
if (mark == 'X') {
return Xs;
}
if (mark == 'O') {
return Os;
}
return BG;
} 和
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();//打印精心制作的“动画”介绍
有了正确的命名,您就不需要对这样的事情发表评论了。
void display_intro();甚至是
void display_elaborate_pseudo_animated_intro_splash();虽然我觉得这太过分了。但是对于一个只调用一次的函数,这种名称是可能的。
我不喜欢使用f_前缀来表示函数。函数一般应该有动词名,因为它们可以做事情。变量有名词名,因为它们代表事物。更常见的做法是使用前缀将代码与其他可能链接的代码(如ttt_display_intro )分开。然后,如果您将您的Tic-Tac-Toe游戏与基于文本的RPG (例如Zork或Rogue)链接起来,那么您可以在这两种游戏中都具有display_intro函数。
在代码示例中,它们经常对每个变量进行注释,以达到教学目的。我觉得这很不幸,因为它使人们对如何使用评论有了不切实际的看法。注释应该用来解释为什么代码会做一些事情,而不是代码正在做什么。代码本身应该足以告诉人们代码正在做什么。
我觉得代码后面的注释在同一行上的做法令人讨厌。我宁愿看到
// Takes the winning character and creates a splash screen declaring victory ('X', 'O', or 'T' for Tie)
void f_declare_winner(char symbol);注意如何去掉滚动条。
如果注释不需要出现在左边的列中,那么没有它可能会更好。
}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'){
使用
input = toupper(getch());
if (input == 'R') {
int r = rand() % 2;
input = (r == 0) ? 'X' : 'O';
}
if (input == 'X') {现在,您不必重复代码。
在功能结束时,
f\_setup();
很可能是
return f_setup();或者将事情更改为使用无限循环而不是递归调用。
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);
}
}
}发布于 2018-12-23 16:33:04
以下是一些可以帮助您改进程序的事情。
中的全局变量
拥有依赖于全局变量的例程使得理解逻辑变得更加困难,并引入了许多错误的机会。对于这个程序来说,将几乎所有的全局变量封装在一个struct中是很简单和自然的,这样就可以清楚地知道哪些事情是一起进行的。然后,您只需要传递一个指向该struct的指针。在某些情况下,例如x,可以简单地将它作为局部变量放入使用该变量的函数中。更好的是,在t的情况下,可以完全消除它,因为time(NULL)可以满足您的需要。
这段代码声明变量px和py,但对它们不做任何操作。如果您知道如何让编译器找到这类问题,那么编译器就足够聪明了。
的可读性
而不是像这样把事情挤在一起:
for(y=0;y<=row;y++){
for(x=0;x<=col;x++){如果你使用更多的空间,大多数人会发现它更容易读懂:
for(y = 0; y <= row; y++) {
for(x = 0;x <= col; x++) {其中有很多重复的代码,还有一些特殊的变量,比如通过space_1通过space_9实现的。只要简单地使用数组space[9]并使用索引变量来遍历这些空间,就可以大大改善这一点。对于过长的f_evaluate_turn()函数,也可以做类似的事情。
返回有用的东西
函数当前的编写方式,大多数返回void,但这妨碍了代码的简化。例如,而不是这样:
f_player_turn();
f_evaluate_turn();
if(game_over == 0){
f_AI_turn();
f_evaluate_turn();
}如果每个回合评估自己并返回true (如果游戏尚未结束),您可以这样写:
if (f_player_turn()) {
f_AI_turn();
}有些名称(如game_over和running )是好的,因为它们是描述性的,但其他名称(如sx )对它们的含义没有给出多少提示。而且,对所有函数使用f_前缀是很烦人的,而且会使代码混乱。
您目前正在使用
which = rand() % 3;这种方法存在一些问题。这会产生比更高的数字更低的数字--这不是一个均匀的分布。另一个问题是随机数发生器的低阶位不是特别随机的,所以结果也不是特别随机。在我的机器上,有一个轻微的,但可测量的偏差,向0。有关详细信息,请参阅这个答案,但我建议将其更改为类似于链接中的内容。在这个特定的应用程序中,这并不重要,但总体上了解是很好的。
如果使用较小的函数,这段代码可以更短、更容易阅读、理解和修改。例如,始终如一地使用这样的函数将大大提高代码的可读性:
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;
}
}发布于 2018-12-23 15:07:59
这些:
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,因为它们不会导出到任何其他模块。
您的running和playing变量实际上是布尔值,因此您应该使用<stdbool.h>。
将x和y作为全局值似乎是不明智的,特别是在循环中使用它们时:
for(y=0;y<=row;y++){
for(x=0;x<=col;x++){它们可能应该保持为局部变量,在这种情况下,应该在循环声明中实例化它们。
您的if(which == 0){可以由一个开关代替,因为您要对它进行三次比较。
char *chose_x和任何其他不改变的字符串都应该声明为const。
这是:
if(input == 'O' || input == 'o')应:
if (tolower(input) == 'o')类似的情况也是如此。
这是:
x = col / 2 - slen / 2;可以是:
x = (col - slen) / 2;不过,正如@PeterCordes正确指出的那样,如果使用有符号整数,则通常需要小心应用此规则。是的,如果您知道数据不会是负的,那么最好将变量设置为unsigned。
这是:
if(yy == 0 || yy % 2 == 0){
mvprintw(y + yy, x, break_lines);
}else{
mvprintw(y + yy, x, play_lines);
}为了简单起见,应该使用中间变量,第一个== 0是冗余的:
char *lines;
if (!(yy % 2))
lines = break_lines;
else
lines = play_lines;
mvprintw(y + yy, x, lines);在Print an "X" in the cell代码中执行类似的临时变量策略。这是以DRY的名义(“不要重复自己”)。
因为您不会修改这个:
char done_splash[] = "Good move!";相反,您应该将其声明为const char*而不是数组。
https://codereview.stackexchange.com/questions/210217
复制相似问题