用Turbo C 2.0写五子棋小游戏

Posted rfisher

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用Turbo C 2.0写五子棋小游戏相关的知识,希望对你有一定的参考价值。

这辈子再也不用Turbo C写东西了_(:зゝ∠)_

功能

  • 有比较友好(大概友好吧:) )的界面。
  • 采用贪心算法,能与计算机对弈

流程图

技术分享图片

主函数

int main()
{
    int gd = DETECT, gm = 0;
    int key;
    initgraph(&gd, &gm, "c:\\tc");

    opencartoon(); /*开场动画*/
    init(); /*界面和棋盘初始化*/
    key = get_key();
    while (!quit) { /*只要没有退出就继续*/
        switch (key) {
        case UP: move(0); break; /*下面四个是上下左右的操作*/
        case DOWN: move(1); break;
        case LEFT: move(2); break;
        case RIGHT: move(3); break;
        case ENTER: /*用户下棋*/
            if (!res && userplace(WHITE)) { /*如果游戏没有结束且用户要下棋了*/
                if ((res = judgewinner(1)) == 0) { /*如果用户没赢*/
                    computerplace(RED); /*则轮到电脑下棋*/
                    res = judgewinner(-1); /*判断电脑赢了吗*/
                }
            }
            break;
        case F1: judgerestart(); break; /*重新开始*/
        case ESC: quit = judgequit(); break; /*退出*/
        }
        if (notchoose && res) { /*游戏结束了且用户没有选择是否继续留在游戏里*/
            if (printres(res)) /*显示游戏结果(输赢),并判断用户是否继续留在游戏里*/
                quit = 1; /*用户不愿意留在游戏里,退出*/
            notchoose = 0; /*用户已做出选择*/
        }
        if (!quit) key = get_key(); /*如果没有退出,继续接受键盘事件*/
    }
    
    cleardevice(); /*清屏*/
    goodbyecartoon(); /*退出动画*/
    closegraph();
    return 0;
}

处理键盘响应

  • Turbo C中用bioskey(),会返回两个值:扫描码和ASCII码
  • 扫描码和ASCII码各占8位
  • 有些特殊的键没有ASCII码,只有扫描码,比如F1键。这时候ASCII码为0
/*键盘宏定义*/
#define UP 0x4800 /*方向键上*/
#define DOWN 0x5000 /*方向键下*/
#define LEFT 0x4b00 /*方向键左*/
#define RIGHT 0x4d00 /*方向键右*/
#define ENTER 13
#define ESC 27
#define F1 0x3B00

/*键值的定义*/
typedef union {
    int word;
    char byte;
}keycode;

/*返回按下的键值*/
int get_key() 
{
    keycode key;
    key.word = bioskey(0); /*bioskey(0)会一直等待有键按下*/
    return key.byte ? key.byte : key.word;
}

界面设计

  • 将对话框的显示封装成函数dialog,允许定制对话框的颜色样式(包括边框颜色),定制文本内容
void init()
{
    char *help_text[] = {"1.ESC - Quit", "2.Direct Key - Move", "3.Enter - Put chess", "4.F1 - Restart",
                         "Note: You move first.", "If there is no place", "to put chess, you", "would LOSE!"};
    
    initialize(); /*初始化棋盘*/
    cleardevice(); /*清屏*/
    drawbackgroud(YELLOW);
    n = m = 10; /*棋盘10行10列*/
    cposx = 50; /*棋盘左上角坐标为(50, 80)*/
    cposy = 80;
    drawchessboard(cposx, cposy, n, m, DARKGRAY); /*画出棋盘*/
    drawtitle(cposx - 3, 10, "======Five in a Row======", BLUE); /*写标题*/
    dialog(cposx + (n + 1) * W + 10, 30, 200, 400, "***Help***", help_text, 8,
           WHITE, BLUE, CYAN, LIGHTGRAY, BLUE, RED); /*帮助文档框*/
    row = 5, col = 5; /*当前准心在第五行第五列*/
    drawtarget(cposx, cposy, row, col, RED); /*绘准心*/
    quit = 0; /*是否退出*/
    res = 0; /*对弈结果*/
    notchoose = 1; /*是否选择留在游戏中*/
    tot = 0; /*下的总步数*/
}

void drawbackgroud(int color)
{
    /*绘制背景*/
    setbkcolor(color);
}

void drawchessboard(int x, int y, int r, int c, int color)
{
    /*绘制棋盘*/
    int height = (r + 1) * W;
    int weight = (c + 1) * W;
    int i;
    setcolor(color);
    line(x, y, x + weight, y);
    line(x, y + height, x + weight, y + height);
    line(x, y, x, y + height);
    line(x + weight, y, x + weight, y + height);
    setlinestyle(DOTTED_LINE, 0, 1); /*虚线*/
    for (i = 1; i <= r; i++)
        line(x, y + i * W, x + weight, y + i * W);
    for (i = 1; i <= c; i++)
        line(x + i * W, y, x + i * W, y + height);
}

void drawtitle(int x, int y, char *title, int color)
{
    /*绘制标题*/
    setcolor(color);
    settextstyle(3, 0, 3);
    outtextxy(x, y, title);
}

void dialog(int x, int y, int width, int height, char *title, char **message, int lines, 
            int bk, int shadow, int bkedge, int shadowedge, int titlec, int messagec)
{
    /*bk对话框颜色、shadow阴影颜色、bkedge边框颜色、shadowedge阴影边框颜色、titlec标题文字颜色、messagec信息文字颜色*/
    int i;
    setcolor(shadowedge);
    setlinestyle(SOLID_LINE, 0, 1);
    rectangle(x + 20, y + 20, x + width + 20, y + height + 20);
    setfillstyle(1, shadow);
    floodfill((2 * x + width + 40) / 2, (2 * y + height + 40) / 2, shadowedge);
    setcolor(bkedge);
    rectangle(x + 10, y + 10, x + width + 10, y + height + 10);
    setfillstyle(1, bk);
    floodfill((2 * x + width) / 2, (2 * y + height) / 2, bkedge);

    setcolor(titlec);
    settextstyle(3, 0, 2);
    outtextxy(x + 20, y + 20, title);

    setcolor(messagec);
    settextstyle(3, 0, 1);
    for (i = 0; i < lines; i++)
        outtextxy(x + 20, y + 20 + 30 * (i + 1), message[i]);
}

用户操作处理

  • 准心移动
  • 用户退出游戏处理和重新开始游戏的处理
/*简化代码*/
const int mover[] = {-1, 1, 0, 0};
const int movec[] = {0, 0, -1, 1};

void move(int type)
{
    /*用户移动准心*/
    int tr = row, tc = col;
    tr += mover[type];
    tc += movec[type];
    
    if (tr == 0 || tr > n || tc == 0 || tc > m) return;
    drawtarget(cposx, cposy, row, col, YELLOW); /*擦除原来的准心*/
    drawtarget(cposx, cposy, tr, tc, RED);
    row = tr;
    col = tc;
}

int judgequit()
{
    /*用户退出操作*/
    int key;
    unsigned size;
    void *buf;
    char *message[] = {"Do you want to quit?(Y/N)"};
    
    /*这里要复制原先的图像,否则如果用户取消就回不去了。。。下面的重新开始游戏也一样*/
    size = imagesize(100, 200, 520, 320);
    buf = malloc(size);
    getimage(100, 200, 520, 320, buf);
    
    dialog(100, 200, 400, 100, "", message, 1, 
           WHITE, BLUE, LIGHTBLUE, GREEN, RED, BLUE);
    key = get_key();
    while (!(key == ‘y‘ || key == ‘n‘ || key == ‘Y‘ || key == ‘N‘)) {
        key = get_key();
    }
    if (key == ‘N‘ || key == ‘n‘)
        putimage(100, 200, buf, COPY_PUT);
    free(buf);
    return (key == ‘y‘ || key == ‘Y‘) ? 1 : 0;
}

int userplace(int color)
{
    /*绘制用户的棋子*/
    int px = cposx, py = cposy;
    if (chess[row][col] == 0) {
        setcolor(WHITE);
        setlinestyle(SOLID_LINE, 0, 1);
        px += col * W;
        py += row * W;
        circle(px, py, W / 4);
        setfillstyle(1, color);
        floodfill(px, py, WHITE);
        chess[row][col] = 1;
        tot++;
        return 1;
    } else 
        return 0;
}

int judgerestart() 
{
    /*重新开始游戏操作*/
    int key;
    unsigned size;
    void *buf;
    char *message[] = {"Do you want to restart?(Y/N)"};
    
    size = imagesize(100, 200, 520, 320);
    buf = malloc(size);
    getimage(100, 200, 520, 320, buf);
    
    dialog(100, 200, 400, 100, "", message, 1, 
           WHITE, BLUE, LIGHTBLUE, GREEN, RED, BLUE);
    key = get_key();
    while (!(key == ‘y‘ || key == ‘n‘ || key == ‘Y‘ || key == ‘N‘)) {
        key = get_key();
    }
    if (key == ‘N‘ || key == ‘n‘)
        putimage(100, 200, buf, COPY_PUT);
    else
        init();
    free(buf);
    return (key == ‘y‘ || key == ‘Y‘) ? 1 : 0;
}

计算机自动下棋——贪心算法实现

  • 计算机的下棋策略是贪心的,可能不如搜索来的智能,但足以挑战初学者
void computerplace(int color)
{
    /*计算机下棋,绘制*/
    int cx = cposx, cy = cposy;
    computerjudge();
    setcolor(WHITE);
    setlinestyle(SOLID_LINE, 0, 1);
    cx += ccol * W;
    cy += crow * W;
    circle(cx, cy, W / 4);
    setfillstyle(1, color);
    floodfill(cx, cy, WHITE);
    chess[crow][ccol] = -1;
    tot++;
}

void computerjudge()
{
    /*得到棋盘上哪一点是“最优的”*/
    int i, j;
    int tppr, tppc, tpcr, tpcc;
    int pscore[MAXN][MAXN], cscore[MAXN][MAXN];
    int mxp = 0, mxc = 0;
    
    memset(pscore, 0, sizeof(pscore));
    memset(cscore, 0, sizeof(cscore));
    for (i = 1; i <= n; i++) {
        for (j = 1; j <= m; j++) if (chess[i][j] == 0) {
                getscore(i, j, pscore, 1);
                getscore(i, j, cscore, -1);
            }
    }
    for (i = 1; i <= n; i++) {
        for (j = 1; j <= m; j++) {
            if (pscore[i][j] > mxp) {
                mxp = pscore[i][j];
                tppr = i, tppc = j;
            }
            if (cscore[i][j] > mxc) {
                mxc = cscore[i][j];
                tpcr = i, tpcc = j;
            }
        }
    }
    if (mxp >= mxc) {
        crow = tppr, ccol = tppc;
    } else {
        crow = tpcr, ccol = tpcc;
    }
}

void getscore(int rr, int cc, int score[MAXN][MAXN], int who)
{
    /*计算棋盘上每个点的分数*/
    int another = -who;
    int tot = 0;
    int num, l, r;
    int tr, tc;
    int i, j;
    
    for (i = 0; i < 4; i++) {
        num = l = r = 0;
        j = 1;
        while (1) {
            tr = rr + j * dirr[i];
            tc = cc + j * dirc[i];
            if (tr > 0 && tc > 0 && tr <= n && tc <= m && chess[tr][tc] == who) {
                num++;
                j++;
            } else {
                if (tr > 0 && tc > 0 && tr <= n && tc <= m && chess[tr][tc] == another)
                    l = 1;
                break;
            }
        }
        j = 1;
        while (1) {
            tr = rr + j * dirr[i + 4];
            tc = cc + j * dirc[i + 4];
            if (tr > 0 && tc > 0 && tr <= n && tc <= m && chess[tr][tc] == who) {
                num++;
                j++;
            } else {
                if (tr > 0 && tc > 0 && tr <= n && tc <= m && chess[tr][tc] == another)
                    r = 1;
                break;
            }
        }
        tot += judgetype(num, l, r);
    }
    score[rr][cc] = tot;
}

int judgetype(int num, int l, int r)
{
    /*每种“类型”的得分*/
    int sco = 0;
    if (num >= 4)
        sco += 10000;
    else if (num == 3) {
        if (!l && !r)
            sco += 3000;
        else if (!(l && r))
            sco += 900;
    } else if (num == 2) {
        if (!l && !r)
            sco += 460;
        else if (!(l && r))
            sco += 30;
    } else if (num == 1) {
        if (!l && !r)
            sco += 45;
        else if (!(l && r))
            sco += 5;
    } else if (num == 0) {
        if (!l && !r)
            sco += 3;
        else if (!(l && r))
            sco += 1;
    }
    return sco;
}

判断胜负

  • 用户先手。棋盘下完,则计算机赢。
/*简化代码*/
const int dirr[] = {-1, 0, -1, -1, 1, 0, 1, 1};
const int dirc[] = {0, -1, -1, 1, 0, 1, 1, -1};

int judgewinner(int who)
{
    int posr, posc;
    int dir[8] = {0};
    int i, j;
    int tr, tc;
    if (who == 1) {
        posr = row;
        posc = col;
    } else {
        posr = crow;
        posc = ccol;
    }
    for (i = 0; i < 8; i++) {
        j = 1;
        while (1) {
            tr = posr + j * dirr[i];
            tc = posc + j * dirc[i];
            if (tr > 0 && tc > 0 && tr <= n && tc <= m && chess[tr][tc] == who) {
                dir[i]++;
                j++;
            } else 
                break;
        }
    }
    for (i = 0; i < 4; i++) {
        if (dir[i] + dir[i + 4] + 1 >= 5) 
            return who;
    }
    if (tot == n * m)
        return -1;
    return 0;
}

退出和进入游戏的动画

  • 这一段代码几乎是抄一个博主写的,但我忘记是哪个博客给的了。。。(五子棋的代码本来也想复制一波的,但这位博主的代码实在不堪入目,自己重写了代码,并加入了计算机自动下棋功能)
  • 凑代码量的东西,很简单:)
/*以下代码只贴“见”的字模*/
char jian64L[]={
/* 以下是 ‘见‘ 的 64点阵隶书 字模,512 byte */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x40,0x00,0x00,0x06,0x00,0x00,
    0x00,0x00,0xF0,0x3F,0xFE,0x1F,0x00,0x00,
    0x00,0x01,0xFF,0xFF,0xFF,0xFF,0x00,0x00,
    0x00,0x01,0xFF,0xFF,0xFF,0xFF,0x80,0x00,
    0x00,0x01,0xFF,0xFF,0xFF,0xFF,0x00,0x00,
    0x00,0x01,0xF8,0x00,0x00,0x1F,0x00,0x00,
    0x00,0x01,0xF0,0x00,0x00,0x1F,0x00,0x00,
    0x00,0x00,0xF0,0x00,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x0E,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1E,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1F,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1F,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1F,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1F,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1F,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1F,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1E,0x00,0x1F,0x00,0x00,
    0x00,0x00,0xF0,0x1E,0x08,0x1F,0x00,0x00,
    0x00,0x00,0xF0,0x1E,0x1C,0x1F,0x00,0x00,
    0x00,0x00,0xF0,0x1E,0x1C,0x1F,0x00,0x00,
    0x00,0x00,0xF0,0x1E,0x38,0x1F,0x80,0x00,
    0x00,0x01,0xF0,0x3C,0x38,0x1F,0x80,0x00,
    0x00,0x00,0xF0,0x3C,0x38,0x1F,0x00,0x00,
    0x00,0x00,0x00,0x3C,0x70,0x0E,0x00,0x00,
    0x00,0x00,0x00,0x78,0x70,0x00,0x00,0x00,
    0x00,0x00,0x00,0xF8,0x70,0x00,0x00,0x00,
    0x00,0x00,0x01,0xF0,0xF0,0x00,0x00,0x00,
    0x00,0x00,0x03,0xE0,0xF0,0x00,0x00,0x00,
    0x00,0x00,0x0F,0xC0,0xF8,0x00,0x00,0x00,
    0x00,0x00,0x3F,0x80,0xFE,0x00,0x00,0x04,
    0x00,0x00,0xFF,0x00,0x7F,0xC0,0x00,0x3C,
    0x00,0x07,0xFE,0x00,0x3F,0xFF,0xFF,0xF8,
    0x00,0x7F,0xF8,0x00,0x1F,0xFF,0xFF,0xF8,
    0x0F,0xFF,0xE0,0x00,0x0F,0xFF,0xFF,0xF0,
    0x1F,0xFF,0x80,0x00,0x03,0xFF,0xFF,0xF0,
    0x0F,0xFC,0x00,0x00,0x00,0x7F,0xFF,0xE0,
    0x07,0xE0,0x00,0x00,0x00,0x07,0xFF,0xC0,
    0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
void tim(int p)
{
    int i;
    for(i = 0; i < p; i++)
        delay(500);
}

void drawmat(char *mat,int matsize,int x,int y,int color)
/*依次:字模指针、点阵大小、起始坐标(x,y)、颜色*/
{
    int i, j, k, n;
    n = (matsize - 1) / 8 + 1;
    for(j = 0; j < matsize; j++) {
        for(i = 0; i < n; i++) {
            for(k = 0;k < 8; k++) {
                if(mat[j * n + i] & (0x80 >> k))  /*测试为1的位则显示*/
                    putpixel(x + i * 8 + k, y + j, color);
            }
        }
    }
}

void opencartoon()
{
    drawmat(huan64L, 64, 149, 200, 1); tim(1);
    drawmat(ying64L, 64, 249, 200, 2); tim(1);
    drawmat(shang64L, 64, 349, 200, 12); tim(1);
    drawmat(wan64L, 64, 449, 200, 4); tim(1);
}

void goodbyecartoon()
{
    drawmat(zai64L, 64, 200, 200, 13); tim(1);
    drawmat(jian64L, 64, 376, 200, 2); tim(1);
}

以上是关于用Turbo C 2.0写五子棋小游戏的主要内容,如果未能解决你的问题,请参考以下文章

[教你做小游戏] 用177行代码写个体验超好的五子棋

五子棋游戏(简单易懂,入门都能学)

详解 C 语言开发五子棋游戏以及游戏中的重要算法与思路

C++实现井字棋小游戏(写得不好,留作纪念!!!)

用C++基础语句写一个五子棋游戏

C++入门级(超级基础)练手小项目----简单五子棋