这款游戏比以前的版本更方便用户--它有一个示例网格来演示输入,现在使用的是Xs和Os。
游戏现在可以检测到抽签。
改变了算法来检测胜利者,虽然游戏结束时仍然有一些组合错误地决定了胜利者或输家--但不知道如何解决这个问题,也不知道是什么原因造成的。
我很想知道如何改进游戏和代码的结构,以及如何避免提到的错误。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// Struct with all game state variables.
struct game_data {
int win; // Either 0 or 1.
int turns; // Ranges from 1 to 9(game end).
int turn; // Either 0 or 1 where 0 is human player
char grid[3][3];
};
//Initialising game state variables
struct game_data game = {
0,
1,
0,
{ { ' ', ' ', ' ' },
{ ' ', ' ', ' ' },
{ ' ', ' ', ' ' } }
};
void intro(void){
printf("Welcome to NOUGHTS AND CROSSES\n\n");
printf("The grid you will be playing on is 3x3 and your input will be determined by the co ordinates you put in, in the form 'row column'.\n\n");
printf("For example an input of '1 1' will put a 'Z' on the first row on the first column. Like so:\n\n");
printf(
"+---+---+---+\n"
"| Z | | |\n"
"+---+---+---+\n"
"| | | |\n"
" ---+---+---|\n"
"| | | |\n"
"+---+---+---+\n"
"\n");
}
void player_one_move(struct game_data* game)
{
int y_val, x_val;
printf("You are 'Crosses'. Please input co-ordinates in the form 'row column' for the 3x3 grid:\n");
scanf(" %d %d", &y_val, &x_val);
if (game->grid[y_val - 1][x_val - 1] == ' ') {
game->grid[y_val - 1][x_val - 1] = 'X';
printf("\nYour turn:\n\n");
}
else {
player_one_move(game);
}
}
void computer_move(struct game_data* game)
{
int x_val = rand() % 3;
int y_val = rand() % 3;
if (game->grid[y_val][x_val] == ' ') {
game->grid[y_val][x_val] = 'O';
printf("\nComputer turn:\n\n");
}
else {
computer_move(game);
}
}
void update(struct game_data* game)
{
printf(
"+---+---+---+\n"
"| %c | %c | %c |\n"
"+---+---+---+\n"
"| %c | %c | %c |\n"
"----+---+---|\n"
"| %c | %c | %c |\n"
"+---+---+---+\n"
"\n",
game->grid[0][0], game->grid[0][1], game->grid[0][2],
game->grid[1][0], game->grid[1][1], game->grid[1][2],
game->grid[2][0], game->grid[2][1], game->grid[2][2]);
}
void game_event_won(struct game_data* game)
{
int count;
//BUGGY
/*char current_mark;
if (game->turn == 0) {
current_mark = 'X';}
else{
current_mark = 'O';
}*/
for (int y_val = 0; y_val < 3; y_val++) {
for (int x_val = 0; x_val < 3; x_val++) {
count = 0;
while (game->grid[y_val][x_val] == 'X') {
x_val++;
count++;
//BUGGY
/*if (count == 3 && current_mark == 'X') {
game->win = 1;
printf("You have WON\n");
}
if (count == 3 && current_mark == 'O') {
game->win = 1;
printf("You have LOST\n");
}*/
if (count == 3) {
game->win = 1;
printf("You have WON\n");
}
}
}
}
for (int x_val = 0; x_val < 3; x_val++) {
for (int y_val = 0; y_val < 3; y_val++) {
count = 0;
while (game->grid[y_val][x_val] == 'X') {
y_val++;
count++;
if (count == 3) {
game->win = 1;
printf("You have WON\n");
}
}
}
}
for (int y_val = 0; y_val < 3; y_val++) {
count = 0;
while (game->grid[y_val][y_val] == 'X') {
count++;
y_val++;
if (count == 3) {
game->win = 1;
printf("You have won\n");
}
}
}
for (int y_val = 0; y_val < 3; y_val++) {
count = 0;
while (game->grid[y_val][2 - y_val] == 'X') {
count++;
y_val++;
if (count == 3) {
game->win = 1;
printf("You have won\n");
}
}
}
}
// Repetition of previous function but for 'O's. Less concise but less buggy than previous implementation.
void game_event_lost(struct game_data* game)
{
int count;
for (int y_val = 0; y_val < 3; y_val++) {
for (int x_val = 0; x_val < 3; x_val++) {
count = 0;
while (game->grid[y_val][x_val] == 'O') {
x_val++;
count++;
if (count == 3) {
game->win = 1;
printf("You have LOST\n");
}
}
}
}
for (int x_val = 0; x_val < 3; x_val++) {
for (int y_val = 0; y_val < 3; y_val++) {
count = 0;
while (game->grid[y_val][x_val] == 'O') {
y_val++;
count++;
if (count == 3) {
game->win = 1;
printf("You have LOST\n");
}
}
}
}
for (int y_val = 0; y_val < 3; y_val++) {
count = 0;
while (game->grid[y_val][y_val] == 'O') {
count++;
y_val++;
if (count == 3) {
game->win = 1;
printf("You have LOST\n");
}
}
}
for (int y_val = 0; y_val < 3; y_val++) {
count = 0;
while (game->grid[y_val][2 - y_val] == 'O') {
count++;
y_val++;
if (count == 3) {
game->win = 1;
printf("You have LOST\n");
}
}
}
}
int main(void)
{
srand((unsigned)time(0));
intro();
while (game.win == 0 ) {
if (game.turn == 0) {
player_one_move(&game);
game.turns++;
game.turn = 1;
}
else {
game.turn = 0;
computer_move(&game);
game.turns++;
}
if (game.turns == 9 && game.win == 0){
game.win = 1;
printf("You have drawn\n");
break;
}
update(&game);
game_event_won(&game);
game_event_lost(&game);
}
return 0;
}编辑:
我已经更改了game_event_won函数。据我所知,它简单得多,没有bug,即使它更丑。可能会让它变短。
void game_event_won(struct game_data* game)
{
if( ((game->grid[0][0] == game->grid[0][1]) && (game->grid[0][1] == game->grid[0][2]) && (game->grid[0][2] == 'X')) ||
((game->grid[1][0] == game->grid[1][1]) && (game->grid[1][1] == game->grid[1][2]) && (game->grid[1][2] == 'X')) ||
((game->grid[2][0] == game->grid[2][1]) && (game->grid[2][1] == game->grid[2][2]) && (game->grid[2][2] == 'X')) ||
((game->grid[0][0] == game->grid[1][0]) && (game->grid[1][0] == game->grid[2][0]) && (game->grid[2][0] == 'X')) ||
((game->grid[0][1] == game->grid[1][1]) && (game->grid[1][1] == game->grid[2][1]) && (game->grid[2][1] == 'X')) ||
((game->grid[0][2] == game->grid[1][2]) && (game->grid[1][2] == game->grid[2][2]) && (game->grid[2][2] == 'X')) ||
((game->grid[0][0] == game->grid[1][1]) && (game->grid[1][1] == game->grid[2][2]) && (game->grid[2][2] == 'X')) ||
((game->grid[0][2] == game->grid[1][1]) && (game->grid[1][1] == game->grid[2][0]) && (game->grid[2][0] == 'X')) ){
game->win = 1;
printf("You have WON\n");
}
if( ((game->grid[0][0] == game->grid[0][1]) && (game->grid[0][1] == game->grid[0][2]) && (game->grid[0][2] == 'O')) ||
((game->grid[1][0] == game->grid[1][1]) && (game->grid[1][1] == game->grid[1][2]) && (game->grid[1][2] == 'O')) ||
((game->grid[2][0] == game->grid[2][1]) && (game->grid[2][1] == game->grid[2][2]) && (game->grid[2][2] == 'O')) ||
((game->grid[0][0] == game->grid[1][0]) && (game->grid[1][0] == game->grid[2][0]) && (game->grid[2][0] == 'O')) ||
((game->grid[0][1] == game->grid[1][1]) && (game->grid[1][1] == game->grid[2][1]) && (game->grid[2][1] == 'O')) ||
((game->grid[0][2] == game->grid[1][2]) && (game->grid[1][2] == game->grid[2][2]) && (game->grid[2][2] == 'O')) ||
((game->grid[0][0] == game->grid[1][1]) && (game->grid[1][1] == game->grid[2][2]) && (game->grid[2][2] == 'O')) ||
((game->grid[0][2] == game->grid[1][1]) && (game->grid[1][1] == game->grid[2][0]) && (game->grid[2][0] == 'O')) ){
game->win = 1;
printf("You have LOST\n");
}
}我还更改了主循环以适应它,防止错误绘制:
int main(void)
{
srand((unsigned)time(0));
intro();
while (game.win == 0 ) {
if (game.turn == 0) {
player_one_move(&game);
game.turns++;
game.turn = 1;
}
else {
game.turn = 0;
computer_move(&game);
game.turns++;
}
update(&game);
game_event_won(&game);
if (game.turns == 10 && game.win == 0){
game.win = 1;
printf("You have DRAWN\n");
break;
}
}
return 0;
}发布于 2019-02-16 19:52:13
用户输入是邪恶的--不管它是邪恶的还是错误的。花费一些精力来验证它。
printf("You are 'Crosses'. Please input co-ordinates ...\n");
// scanf(" %d %d", &y_val, &x_val);
if (scanf(" %d %d", &y_val, &x_val) != 2) {
puts("Invalid input");
exit(EXIT_FAILURE);
}
if (y_val < 1 || y_val > 3 || x_val < 1 || x_val > 3) {
puts("Out of range input");
exit(EXIT_FAILURE);
}更好的代码将有选择地允许重新输入。
//BUGGY注释在这里张贴工作代码。对于您在解决问题时遇到的错误,请考虑堆栈溢出。
const不改变其指向数据的函数使用const编写得更好。这允许更清晰的功能界面,更广泛的应用,更多的编译器时间检查和潜在的更好的优化。
// void update(struct game_data* game)
void update(const struct game_data* game)而不是使用if( ((game->grid[0][0] == game->grid[0][1]) ...使用'X'的8行代码,然后再用'O'生成8行类似的代码,形成一个辅助函数。
char three_in_row(const char g[][3]) {
if(g[0][0] != ' ' && g[0][0] == g[0][1] && g[0][1] == g[0][2]) return g[0][0];
if(g[1][0] != ' ' && g[1][0] == g[1][1] && g[1][1] == g[1][2]) return g[1][0];
if(g[2][0] != ' ' && g[2][0] == g[2][1] && g[2][1] == g[2][2]) return g[2][0];
if(g[0][0] != ' ' && g[0][0] == g[1][0] && g[1][0] == g[2][0]) return g[0][0];
if(g[0][1] != ' ' && g[0][1] == g[1][1] && g[1][1] == g[2][1]) return g[0][1];
if(g[0][2] != ' ' && g[0][2] == g[1][2] && g[1][2] == g[2][2]) return g[0][2];
if(g[0][0] != ' ' && g[0][0] == g[1][1] && g[1][1] == g[2][2]) return g[0][0];
if(g[0][2] != ' ' && g[0][2] == g[1][1] && g[1][1] == g[2][0]) return g[0][2];
return ' ';
}
void game_event_won(struct game_data* game) {
switch (three_in_row(game->grid)) {
case 'X': game->win = 1; printf("You have WON\n"); break;
case 'O': game->win = 1; printf("You have LOST\n"); break;
}
}而不是game_event_won(),考虑一个从-100到100的TTT_rate(game)函数。
0: tie
100: X won
-100: O won
1...99: X winning
-1...-99: O winning在computer_move(struct game_data* game)中使用循环
看起来范围是1.10。
int turns; // Ranges from 1 to 9(game end).
if (game.turns == 10 && game.win == 0){我希望while块会缩进。
提示:使用自动格式化程序。手动格式化是无效的。
while (game.win == 0 ) {
// if (game.turn == 0) {
if (game.turn == 0) {
...将struct game_data game从全局移动到main()内部。
考虑允许用户或计算机先行。
为X和O移动调用一个函数。函数对可以来自user_move()、computer_move()、smarter_computer_move1()、smarter_computer_move2()等,这将允许2播放,计算机算法1与算法2,用户与不同级别的游戏,等等。
https://codereview.stackexchange.com/questions/213547
复制相似问题