c語(yǔ)言實(shí)現(xiàn)三子棋

2023-01-11 11:20:17 來(lái)源:51CTO博客

前言:

在此之前我們學(xué)習(xí)了循環(huán),函數(shù),數(shù)組等相關(guān)知識(shí),我們來(lái)寫(xiě)一個(gè)小游戲練練手

概述:

代碼大致分為三部分程序主函數(shù),函數(shù),聲明函數(shù)(這一點(diǎn)我們?cè)谕ㄓ嶄涰?xiàng)目是就介紹過(guò)了,將代碼分為三部分可以是代碼各司其職,不冗余,可讀性高),游戲框架大概分為以下幾部分,主函數(shù)main,菜單函數(shù),初始化棋盤(pán),打印棋盤(pán),玩家走函數(shù),電腦走函數(shù),判斷輸贏函數(shù),所以我們也會(huì)按照這幾個(gè)部分來(lái)講解,先給大家演示一下,我們是通過(guò)輸入坐標(biāo)的形式來(lái)落子

程序主函數(shù)(course-8.c)

main程序入口

我們先搭建好程序的主體部分,然后在慢慢添加相應(yīng)功能,這里進(jìn)入主函數(shù)然后調(diào)用test函數(shù)


(資料圖片)

int main(){  test();  return 0;}

test函數(shù)

在test函數(shù)中我們可以加入菜單和根據(jù)用戶(hù)輸入判斷出對(duì)應(yīng)的不同提示語(yǔ)句,

srand((unsigned int)time(NULL));這句代碼我們?cè)诓聰?shù)字游戲是就介紹過(guò),他是用來(lái)生成隨機(jī)數(shù)的(等函數(shù)部分涉及到我們?cè)谥v解),先說(shuō)結(jié)構(gòu),利用一個(gè)do...while循環(huán)嵌套switch....case語(yǔ)句,根據(jù)用戶(hù)輸入判斷出相應(yīng)的功能(這里說(shuō)明一點(diǎn)將0作為退出結(jié)束,是因?yàn)?在c語(yǔ)言中可以作為假,在進(jìn)行while(0)即可跳出do...while循環(huán))

void test(){  int input = 0;  srand((unsigned int)time(NULL));  do  {    menu();    printf("請(qǐng)選擇:");    scanf("%d", &input);    switch (input)    {    case 1:      printf("游戲開(kāi)始\n");      game();      break;    case 0:      printf("退出游戲\n");      break;    default:      printf("輸入錯(cuò)誤,請(qǐng)重新輸入\n");      break;    }  } while (input);}

menu函數(shù)

void menu(){  printf("********************************\n");  printf("******   1 .play 0.exit   ******\n");  printf("********************************\n");}

game函數(shù)

在game中涉及到初始化棋盤(pán),打印棋盤(pán),玩家走函數(shù),電腦走函數(shù),判斷輸贏函數(shù)的調(diào)用,首先進(jìn)入函數(shù)設(shè)置棋盤(pán)信息,然后初始化棋盤(pán)(目的是將棋盤(pán)置空),然后是打印棋盤(pán),我們來(lái)探討一下重點(diǎn),就是判斷輸贏一共有四種狀態(tài)

*代表玩家贏

#代表電腦贏

q代表平局

c代表繼續(xù)

通過(guò)調(diào)用函數(shù)iswin得到的返回值來(lái)判斷是誰(shuí)贏還是繼續(xù)或者平局,判斷機(jī)制是玩家或者電腦每下一步棋就進(jìn)行一次判斷,while循環(huán)是為了讓玩家和電腦循環(huán)下棋,如果得到的返回值不得c了,即游戲不在繼續(xù),利用break跳出循環(huán),根據(jù)返回值判斷出對(duì)弈結(jié)果

void game(){  char ret = 0;  char board[ROW][COL] = { 0 };//數(shù)組存放走出的棋盤(pán)信息  initboard(board, ROW, COL);//初始化棋盤(pán)  displayboard(board,ROW,COL);//打印棋盤(pán)  while (1)  {    //玩家下棋    playermove(board, ROW, COL);    displayboard(board, ROW, COL);//打印棋盤(pán)    //判斷玩家是否贏    ret=iswin(board,ROW,COL);    if (ret != "c")    {      break;    }    //電腦下棋    computermove(board, ROW, COL);    displayboard(board, ROW, COL);//打印棋盤(pán)    //判斷電腦是否贏    ret=iswin(board, ROW, COL);    if (ret != "c")    {      break;    }  }  if (ret == "*")    printf("玩家贏\n");  else if (ret == "#")    printf("電腦贏\n");  else    printf("平局\n");}

程序的頭文件即函數(shù)聲明(game.h)

函數(shù)聲明

這里特別聲明一點(diǎn)define宏定義,之前我們沒(méi)有把棋盤(pán)的row和col寫(xiě)死,而是利用一個(gè)變量,這里的變量就利用了宏定義,(我們單獨(dú)將函數(shù)的聲明寫(xiě)在一個(gè)文件里是因?yàn)榭梢栽鰪?qiáng)函數(shù)的可讀性)這樣只需要在主函數(shù)文件(course-8.c)的頂部引用#include "game.h"即可

#define ROW 3#define COL 3#include#include#include//函數(shù)的聲明void initboard(char board[ROW][COL], int row, int col);void displayboard(char board[ROW][COL], int rw, int col);void playermove(char board[ROW][COL], int row, int col);void computermove(char board[ROW][COL], int row, int col);char iswin(char board[ROW][COL], int row, int col);

函數(shù)部分(game.c)

我們按照順序依次介紹

棋盤(pán)初始化initboard

利用兩個(gè)for循環(huán)把每一個(gè)位置置為空

void initboard(char board[ROW][COL], int row, int col)//初始化函數(shù)體{  int i = 0;  for (i = 0; i < row; i++)  {    int j = 0;    for (j = 0; j < col; j++)    {      board[i][j] = " ";//給棋盤(pán)初始化成空格    }  }}

打印棋盤(pán)displayboard

首先展示一下我們棋盤(pán)的樣子,他是由sanbufenzucheng,數(shù)據(jù),| ,___ ,

我們是按行打印,首先我們利用for循環(huán)的嵌套先打印第一行數(shù)據(jù),因?yàn)?“ | ”一行只需要兩個(gè)所以if(j < col - 1),打印一行數(shù)據(jù)后換行,接著打印第二行也就是分割線" ___ ",(這里提示row是控制行,而col則是控制列)

void displayboard(char board[ROW][COL], int row, int col)//打印棋盤(pán)優(yōu)化函數(shù){  int i = 0;  for (i = 0; i < row; i++)  {    int j = 0;    ////////////1.打印一行數(shù)據(jù)    for (j = 0; j < col; j++)       {      printf(" %c ",board[i][j]);      if(j < col - 1)      printf("|");    }    printf("\n");    /////////////2.打印分割行    if (i < row - 1)    {      for (j = 0; j < col; j++)      {        printf("---");        if (j < col - 1)          printf("|");      }      printf("\n");    }  }}

玩家落子playermove

玩家走起的邏輯也很簡(jiǎn)單,就一個(gè)循環(huán),之所以使用循環(huán)是因?yàn)橥婕铱赡芟碌淖鴺?biāo)不合法,所以首先我們使用一個(gè)while循環(huán),讓玩家落子,直接判斷其坐標(biāo)是否合法if (x >= 1 && x <= row && y >= 1 && y <= col),因?yàn)槠遄右湓谄灞P(pán)內(nèi)且為空,如果條件滿(mǎn)足則合法可以落子,否則循環(huán)回去重新下,如下,由于我們的數(shù)組是從0開(kāi)始計(jì)數(shù)的但是玩家不知道,所以在玩家下完棋后我們需要將坐標(biāo)減1才行

void playermove(char board[ROW][COL], int row, int col)//玩家走函數(shù){  int x = 0;  int y = 0;  printf("玩家走\(yùn)n");  while (1)  {    printf("請(qǐng)輸入要下的坐標(biāo):");    scanf("%d%d", &x, &y);    //判斷坐標(biāo)合法性    if (x >= 1 && x <= row && y >= 1 && y <= col)    {      if (board[x - 1][y - 1] == " ")      {        board[x - 1][y - 1] = "*";        break;      }      else      {        printf("該坐標(biāo)已被占用\n");      }    }    else    {      printf("該坐標(biāo)非法,請(qǐng)重新輸入\n");    }  }}

電腦落子computermove

電腦落子和玩家落子邏輯其實(shí)差不多,要坐標(biāo)合法,重點(diǎn)就是生成隨機(jī)數(shù)的問(wèn)題,我們利用rand函數(shù)生成隨機(jī)數(shù)(rand需要srand函數(shù)的調(diào)用,俄日srand又需要時(shí)間戳的來(lái)生成隨機(jī)數(shù)列,即srand((unsigned int)time(NULL));),通過(guò)處理rand() % row;則可以是隨機(jī)數(shù)達(dá)到我們想要的范圍,rand()%n,范圍肯定是0~n-1即0-2,這里可能就有小伙伴問(wèn)了,不是需要1-3的數(shù)嘛,大家仔細(xì)想一想,我們的數(shù)組是從0開(kāi)始的

void computermove(char board[ROW][COL], int row, int col)//電腦走函數(shù){  int x = 0;  int y = 0;  printf("電腦走\(yùn)n");  while (1)  {    x = rand() % row;    y = rand() % col;    if (board[x][y] == " ")    {      board[x][y] = "#";      break;    }  }}

判斷棋盤(pán)滿(mǎn)isfull

如果棋盤(pán)滿(mǎn)了還沒(méi)分出勝負(fù),那就是平局唄,返回值0代表沒(méi)滿(mǎn),1代表滿(mǎn)了,我們遍歷棋盤(pán)但凡是碰到一個(gè)空就是沒(méi)滿(mǎn)返回0,否則返回1

//判斷棋盤(pán)滿(mǎn)沒(méi)滿(mǎn)//返回1滿(mǎn)了//返回0沒(méi)滿(mǎn)int isfull(char board[ROW][COL], int row, int col){  int i = 0;  int j = 0;  for (i = 0; i < row; i++)  {    for (j = 0; j < col; j++)    {      if (board[i][j] == " ")        return 0;//沒(méi)滿(mǎn)    }  }  return 1;//滿(mǎn)了}

判斷輸贏iswin

判斷輸贏是整個(gè)游戲的核心,也是代碼最復(fù)雜的,大家要多動(dòng)腦思考其中的邏輯,首先我們進(jìn)入函數(shù),先判斷行存不存在贏棋,我們利用for循環(huán)一行一行的判斷,根據(jù)畫(huà)圖總結(jié)一行只需要判斷兩次即可確定,定義一個(gè)判斷返回標(biāo)志為int count = 0;//用來(lái)記錄兩次判斷的結(jié)果(因?yàn)橐恍幸袛鄡纱危?,因?yàn)橐恍信袛鄡纱嗡詂ol-1,例如第一行 第一次00=01&&00!=‘ ’第二次01=02&&01!= ‘ ’,一行判斷兩次,如果count值為2則證明此行數(shù)據(jù)相同,則返回本行的第一個(gè)數(shù)據(jù)在主函數(shù)中的game函數(shù)中根據(jù)相關(guān)邏輯給出相應(yīng)提示并結(jié)束游戲,判斷列,和判斷行相同原理,只是行列顛倒

接下來(lái)就是判斷斜行,左斜行,count一樣用來(lái)記錄兩次判斷的結(jié)果,即00=11&&00!="" 和 11=22&&11!=‘’,此時(shí)的for循環(huán)就有需要兩個(gè)變量了,做到同增同減

右斜行判斷 02=11&&02!="" 和 11=20&&11!=‘’

如果,行列斜行都判斷過(guò)了均沒(méi)有返回,就剩下兩種可能,平局或者游戲繼續(xù)

棋盤(pán)滿(mǎn)了則平局,否則游戲繼續(xù)

char iswin(char board[ROW][COL], int row, int col)//判斷輸贏    {  int i = 0;    //判斷行  for (i = 0; i < row; i++)  {    int count = 0;//用來(lái)記錄兩次判斷的結(jié)果(因?yàn)橐恍幸袛鄡纱危?   int j = 0;    for (j = 0; j < col-1; j++)//一行判斷兩次所以col-1,例如第一行 第一次00=01&&00!=‘’第二次01=02&&01!=‘’    {      if (board[i][j] == board[i][j+ 1] && board[i][j] != " ")      {        count++;//符合條件count++      }    }    if (count == col - 1)//一行判斷兩次,如果count值為2則證明此行數(shù)據(jù)相同    {      return board[i][0];//返回本行的第一個(gè)數(shù)據(jù)    }  }  //判斷列,和判斷行相同原理,只是行列顛倒  for (i = 0; i < col; i++)  {    int count = 0;    int j = 0;    for (j = 0; j < row-1; j++)    {      if (board[j][i] == board[j+1][i] && board[j][i] != " ")      {        count++;      }    }    if (count == row - 1)    {      return board[0][i];    }  }  //判斷兩斜行  //左斜行  int j = 0;  int count = 0;//用來(lái)記錄兩次判斷的結(jié)果  for (i = 0,j=0; i < row-1; i++,j++)//00=11&&00!="" 和 11=22&&11!=‘’  {        if (board[i][j] == board[i + 1][j + 1] && board[i][j] != " ")    {      count++;    }    if (count == row-1)    {      return board[i][j];    }  }  //右斜行  int counts = 0;  for (i = 0, j = row - 1; i < row - 1; i++, j--)//02=11&&02!="" 和 11=20&&11!=‘’  {    if (board[i][j] == board[i + 1][j - 1] && board[i][j] != " ")    {      counts++;    }    if (counts == row - 1)    {      return board[i][j];    }  }  //平局  if (isfull(board, ROW, COL) == 1)  {    return "q";//平局  }  return "c";    //繼續(xù)}

全部代碼

主函數(shù)文件course-8.c

#define _CRT_SECURE_NO_WARNINGS 1#include#include#include "game.h"http://三子棋游戲void game(){  char ret = 0;  char board[ROW][COL] = { 0 };//數(shù)組存放走出的棋盤(pán)信息  initboard(board, ROW, COL);//初始化棋盤(pán)  displayboard(board,ROW,COL);//打印棋盤(pán)  while (1)  {    //玩家下棋    playermove(board, ROW, COL);    displayboard(board, ROW, COL);//打印棋盤(pán)    //判斷玩家是否贏    ret=iswin(board,ROW,COL);    if (ret != "c")    {      break;    }    //電腦下棋    computermove(board, ROW, COL);    displayboard(board, ROW, COL);//打印棋盤(pán)    //判斷電腦是否贏    ret=iswin(board, ROW, COL);    if (ret != "c")    {      break;    }  }  if (ret == "*")    printf("玩家贏\n");  else if (ret == "#")    printf("電腦贏\n");  else    printf("平局\n");}void menu(){  printf("********************************\n");  printf("******   1 .play 0.exit   ******\n");  printf("********************************\n");}void test(){  int input = 0;  srand((unsigned int)time(NULL));  do  {    menu();    printf("請(qǐng)選擇:");    scanf("%d", &input);    switch (input)    {    case 1:      printf("游戲開(kāi)始\n");      game();      break;    case 0:      printf("退出游戲\n");      break;    default:      printf("輸入錯(cuò)誤,請(qǐng)重新輸入\n");      break;    }  } while (input);}int main(){  test();  return 0;}

函數(shù)聲明game.h

#define ROW 3#define COL 3#include#include#include//函數(shù)的聲明void initboard(char board[ROW][COL], int row, int col);void displayboard(char board[ROW][COL], int rw, int col);void playermove(char board[ROW][COL], int row, int col);void computermove(char board[ROW][COL], int row, int col);char iswin(char board[ROW][COL], int row, int col);

函數(shù)部分game.c

#define _CRT_SECURE_NO_WARNINGS 1#include "game.h"void initboard(char board[ROW][COL], int row, int col)//初始化函數(shù)體{  int i = 0;  for (i = 0; i < row; i++)  {    int j = 0;    for (j = 0; j < col; j++)    {      board[i][j] = " ";//給棋盤(pán)初始化成空格    }  }}//void displayboard(char board[ROW][COL], int row, int col)//打印棋盤(pán)函數(shù)//{//  int i = 0;//  for (i = 0; i < row; i++)//  {//    printf(" %c | %c | %c \n",board[i][0],board[i][1],board[i][2]);//打印一行數(shù)據(jù)//    if (i < row - 1)//    {//      printf("---|---|---\n");   //打印分割行//    }//  }//}void displayboard(char board[ROW][COL], int row, int col)//打印棋盤(pán)優(yōu)化函數(shù){  int i = 0;  for (i = 0; i < row; i++)  {    int j = 0;    ////////////1.打印一行數(shù)據(jù)    for (j = 0; j < col; j++)       {      printf(" %c ",board[i][j]);      if(j < col - 1)      printf("|");    }    printf("\n");    /////////////2.打印分割行    if (i < row - 1)    {      for (j = 0; j < col; j++)      {        printf("---");        if (j < col - 1)          printf("|");      }      printf("\n");    }  }}//   |   |   //---|---|---//   |   |   //---|---|---//   |   |   void playermove(char board[ROW][COL], int row, int col)//玩家走函數(shù){  int x = 0;  int y = 0;  printf("玩家走\(yùn)n");  while (1)  {    printf("請(qǐng)輸入要下的坐標(biāo):");    scanf("%d%d", &x, &y);    //判斷坐標(biāo)合法性    if (x >= 1 && x <= row && y >= 1 && y <= col)    {      if (board[x - 1][y - 1] == " ")      {        board[x - 1][y - 1] = "*";        break;      }      else      {        printf("該坐標(biāo)已被占用\n");      }    }    else    {      printf("該坐標(biāo)非法,請(qǐng)重新輸入\n");    }  }}void computermove(char board[ROW][COL], int row, int col)//電腦走函數(shù){  int x = 0;  int y = 0;  printf("電腦走\(yùn)n");  while (1)  {    x = rand() % row;    y = rand() % col;    if (board[x][y] == " ")    {      board[x][y] = "#";      break;    }  }}//判斷棋盤(pán)滿(mǎn)沒(méi)滿(mǎn)//返回1滿(mǎn)了//返回0沒(méi)滿(mǎn)int isfull(char board[ROW][COL], int row, int col){  int i = 0;  int j = 0;  for (i = 0; i < row; i++)  {    for (j = 0; j < col; j++)    {      if (board[i][j] == " ")        return 0;//沒(méi)滿(mǎn)    }  }  return 1;//滿(mǎn)了}//一共有四種狀態(tài)//*代表玩家贏//#代表電腦贏//q代表平局//c代表繼續(xù)//char iswin(char board[ROW][COL], int row, int col)//判斷輸贏//{//  int i = 0;//  //判斷橫三行//  for (i = 0; i < row; i++)//  {//    if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != " ")//    {//      return board[i][1];//    }//  }//  //判斷豎三列//  for (i = 0; i < col; i++)//  {//    if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != " ")//    {//      return board[1][i];//    }//  }//  //判斷兩斜行//  if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != " ")//    return board[1][1];//  if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != " ")//    return board[1][1];//  if (isfull(board, ROW, COL) == 1)//  {//    return "q";//平局//  }//  return "c";    //繼續(xù)//}char iswin(char board[ROW][COL], int row, int col)//判斷輸贏    自己寫(xiě)的優(yōu)化代碼{  int i = 0;    //判斷行  for (i = 0; i < row; i++)  {    int count = 0;//用來(lái)記錄兩次判斷的結(jié)果(因?yàn)橐恍幸袛鄡纱危?   int j = 0;    for (j = 0; j < col-1; j++)//一行判斷兩次所以col-1,例如第一行 第一次00=01&&00!=‘’第二次01=02&&01!=‘’    {      if (board[i][j] == board[i][j+ 1] && board[i][j] != " ")      {        count++;//符合條件count++      }    }    if (count == col - 1)//一行判斷兩次,如果count值為2則證明此行數(shù)據(jù)相同    {      return board[i][0];//返回本行的第一個(gè)數(shù)據(jù)    }  }  //判斷列,和判斷行相同原理,只是行列顛倒  for (i = 0; i < col; i++)  {    int count = 0;    int j = 0;    for (j = 0; j < row-1; j++)    {      if (board[j][i] == board[j+1][i] && board[j][i] != " ")      {        count++;      }    }    if (count == row - 1)    {      return board[0][i];    }  }  //判斷兩斜行  //左斜行  int j = 0;  int count = 0;//用來(lái)記錄兩次判斷的結(jié)果  for (i = 0,j=0; i < row-1; i++,j++)//00=11&&00!="" 和 11=22&&11!=‘’  {        if (board[i][j] == board[i + 1][j + 1] && board[i][j] != " ")    {      count++;    }    if (count == row-1)    {      return board[i][j];    }  }  //右斜行  int counts = 0;  for (i = 0, j = row - 1; i < row - 1; i++, j--)//02=11&&02!="" 和 11=20&&11!=‘’  {    if (board[i][j] == board[i + 1][j - 1] && board[i][j] != " ")    {      counts++;    }    if (counts == row - 1)    {      return board[i][j];    }  }  //平局  if (isfull(board, ROW, COL) == 1)  {    return "q";//平局  }  return "c";    //繼續(xù)}

留言:

歡迎評(píng)論區(qū)交流哦

標(biāo)簽: 首先我們

上一篇:Python網(wǎng)絡(luò)編程之TCP網(wǎng)絡(luò)應(yīng)用程序開(kāi)發(fā)流程
下一篇:世界最資訊丨TiDB 底層存儲(chǔ)結(jié)構(gòu) LSM 樹(shù)原理介紹