usr 发表于 2020-10-31 23:08:34

【C】基于控制台的拼图小游戏

本帖最后由 new_starter 于 2020-10-31 23:24 编辑

发一个基于控制台的拼图小游戏。
这个游戏来源于 StoneValley源码.
这个游戏需要StoneValley库的支持,它用到了栈和矩阵。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <math.h>
#include "../src/svstring.h"
#include "../src/svstack.h"

/* The following structure defines game actions in undo list. */
typedef struct st_Action {
        size_t x;
        size_t y;
} ACTION;

/* Strings that consist squares in a puzzle. */
const static char * const pAlphabet    = " ABCDEFGHIJKLMNOPQRSTUVWX";
const static char * const pInscription = " nullum_numen_abest_si_sit_prudentia";
const static char * const pValleyMap   = " Librarysss_ALGs___ORIsss_THM__stonesss_\\_/yellaV";
static const char * pAnswer = NULL;

static STACK_L stkAction = NULL; /* Action list. */
static volatile size_t gCtrMove = 0; /* Number of times of movements. */

// Function: PrintMatrixInMat
// Desc:   Both print the matrix presented puzzle and its contour.
// Param:    pmtx: Pointer to a matrix presented puzzle.
// Return:   N/A.
void PrintMatrixInMat(P_MATRIX pmtx)
{
        register char * pc = (char *)(pAnswer + 1);
        register size_t i, j;
        /* Print header. */
        printf("");
        for (i = 0; i < pmtx->col; ++i)
                putc(i + 'a', stdout);
        printf("Move: %d.\n +", gCtrMove);
        for (i = 0; i < pmtx->col; ++i)
                putc('-', stdout);
        printf("+\n");
        /* Print puzzle body. */
        for (i = 0; i < pmtx->ln; ++i)
        {
                printf("%c|", i + 'a');
                for (j = 0; j < pmtx->col; ++j)
                        printf("%c", *(char *)strGetValueMatrix(NULL, pmtx, i, j, sizeof(char)));
                printf("| ");
                for (j = 0; j < pmtx->col; ++j)
                        if (!(i == pmtx->ln - 1 && j == pmtx->col - 1))
                                putc(*pc++, stdout);
                printf(" \n");
        }
        /* Print tail. */
        printf(" +");
        for (i = 0; i < pmtx->col; ++i)
                putc('-', stdout);
        printf("+\n");
}

// Function: ValidateMatrix
// Desc:   Validate a puzzle to check whether it has been finished.
// Param:    pmtx: Pointer to a matrix presented puzzle.
// Return:   TRUE: Solved; FALSE Not solved.
BOOL ValidateMatrix(P_MATRIX pmtx)
{
        if (*(char *)strGetValueMatrix(NULL, pmtx, pmtx->ln - 1, pmtx->col - 1, sizeof(char)) == 32)
        {        /* Only the last square in the puzzle is a space can indicate that the puzzle might be solved. */
                if (0 == memcmp(pAnswer + 1, pmtx->arrz.pdata, strLevelArrayZ(&pmtx->arrz) - 1))
                {        /* Good job. */
                        printf(":) CONGRATULATIONS! You win the game in %d movement%c!\n\n", gCtrMove, gCtrMove > 1 ? 's' : '!');
                        return TRUE;
                }
        }
        return FALSE;
}

// Function: MoveMatrix
// Desc:   Take an action on a matrix presented puzzle.
// Param:    pmtx: Pointer to a matrix presented puzzle.
//         x:    Line that uses want to hit.
//         y:    Column that uses want to hit.
//         bundo:TRUE Undoing movement; FALSE Normal movement.
// Return:   N/A.
void MoveMatrix(P_MATRIX pmtx, size_t x, size_t y, BOOL bundo)
{
        register char * pchar = (char *)strGetValueMatrix(NULL, pmtx, x, y, sizeof(char));
        if (NULL != pchar)
        {        /* x and y are in range. */
                if (! isspace(*pchar))
                {        /* If the current hit square is not a space,
                       * then take it as the center of a crucifix,
                       * check whether there is a space in the line of the crucifix
                       * or on the column of the crucifix. If it does,
                       * move squares toward the space.
                       */
                        ACTION act;
                        auto char c;
                        register size_t i, j;
                        /* Check current column. */
                        for (i = 0; i < pmtx->col; ++i)
                        {
                                if (isspace(*(char *)strGetValueMatrix(NULL, pmtx, i, y, sizeof(char))))
                                {
                                        if (! bundo)
                                        {
                                                act.x = i;
                                                act.y = y;
                                                stkPushL(&stkAction, &act, sizeof(ACTION));
                                                ++gCtrMove;
                                        }
                                        /* Move bricks toward the space. */
                                        if (i < x)
                                                for (j = i; j < x; ++j)
                                                        svSwap(strGetValueMatrix(NULL, pmtx, j, y, sizeof(char)),
                                                        strGetValueMatrix(NULL, pmtx, j + 1, y, sizeof(char)), &c, sizeof(char));
                                        else
                                                for (j = i; j > x; --j)
                                                        svSwap(strGetValueMatrix(NULL, pmtx, j, y, sizeof(char)),
                                                        strGetValueMatrix(NULL, pmtx, j - 1, y, sizeof(char)), &c, sizeof(char));
                                        return;
                                }
                        }
                        /* Check current line. */
                        for (i = 0; i < pmtx->ln; ++i)
                        {
                                if (isspace(*(char *)strGetValueMatrix(NULL, pmtx, x, i, sizeof(char))))
                                {
                                        if (! bundo)
                                        {
                                                act.x = x;
                                                act.y = i;
                                                stkPushL(&stkAction, &act, sizeof(ACTION));
                                                ++gCtrMove;
                                        }
                                        /* Move bricks toward the space. */
                                        if (i < y)
                                                for (j = i; j < y; ++j)
                                                        svSwap(strGetValueMatrix(NULL, pmtx, x, j, sizeof(char)),
                                                        strGetValueMatrix(NULL, pmtx, x, j + 1, sizeof(char)), &c, sizeof(char));
                                        else
                                                for (j = i; j > y; --j)
                                                        svSwap(strGetValueMatrix(NULL, pmtx, x, j, sizeof(char)),
                                                        strGetValueMatrix(NULL, pmtx, x, j - 1, sizeof(char)), &c, sizeof(char));
                                        return;
                                }
                        }
                }
        }
        printf(":( You have hit the wrong place at (%c,%c).\n", x <= 26 ? x + 'a' : '?', y <= 26 ? y + 'a' : '?');
}

// Function: UndoMoving
// Desc:   Undo an action on a puzzle.
// Param:    pmtx: Pointer to a matrix presented puzzle.
// Return:   N/A.
void UndoMoving(P_MATRIX pmtx)
{
        if (! stkIsEmptyL(&stkAction))
        {
                ACTION act;
                stkPopL(&act, sizeof(ACTION), &stkAction);
                MoveMatrix(pmtx, act.x, act.y, TRUE); /* Restore previous status. */
                --gCtrMove;
        }
        else
                printf("Undo list is empty.\n");
}

// Function: ShowHelp
// Desc:   Show help information.
// Param:    b: TRUE Show welcome text; FALSE Show usage.
// Return:   N/A.
void ShowHelp(BOOL b)
{
        switch (b)
        {
        case FALSE:
                printf("Welcome to Puzzle at ");
                svPrintVersion();
                printf(".\n\tPlease input:\n\t16: playing a 4*4 puzzle. (Alphabet)\n\t25: playing a 5*5 puzzle. (Alphabet)\n\
\t36: playing a 6*6 puzzle. (Inscription)\n\t49: playing a 7*7 puzzle. (Map)\n\t 0: Exit.\n?> ");
                break;
        case TRUE:
                printf("\tInput 'xy' to hit a square in the puzzle,\n\
\twhereas x denotes line, y denotes column.\n\tInput 'z' to undo.\n\
\tInput '0' or 'q' to quit.\n\tInput 'h' or \'?\' for help.\n\
\tA sequence of actions is allowed to input, such that \'aadaq?dd\'.\n");
        }
}

// Function: CommandParser
// Desc:   Parse a string of command sequence.
// Param:    pmtx: Pointer to a matrix presented puzzle.
//         pcmd: A string that contains commands.
// Return:   0: Command parsed succeeded. 1: Escaping required.
int CommandParser(P_MATRIX pmtx, char * pcmd)
{
        int i = 0, r = 0;
        BOOL bhelp = FALSE;
        do
        {
                if ('\0' == pcmd || '\n' == pcmd) /* Buffer end. */
                {
                        if (1 != r)
                                r = 0;
                        break;
                }
                if ('0' == pcmd || 'q' == pcmd) /* Quit. */
                {
                        r = 1;
                        ++pcmd;
                        continue;
                }
                pcmd = tolower(pcmd);
                pcmd = tolower(pcmd);
                if ('z' == pcmd)
                {        /* Undo. */
                        UndoMoving(pmtx);
                        ++pcmd;
                        continue;
                }
                if (('h' == pcmd) || ('?' == pcmd))
                {        /* Help. */
                        if (FALSE == bhelp)
                                ShowHelp(bhelp = TRUE);
                        ++pcmd;
                        continue;
                }
                /* Translate the current command. */
                MoveMatrix(pmtx, pcmd - 'a', pcmd - 'a', FALSE);
                pcmd += 2;
                ++i;
        }
        while (i < BUFSIZ);
        PrintMatrixInMat(pmtx);
        return r;
}

// Function: main
// Desc:   Program entry.
// Param:    N/A.
// Return:   0 only.
signed int main(void)
{
        MATRIX mtxPuzzle;
        auto char t, ipb = { 0 }; /* Input buffer. */
        stkInitL(&stkAction);
Lbl_Again:
        for (gCtrMove = 0; ; )
        {        /* Select a mode to play or quit game. */
                ShowHelp(FALSE);
                fgets(ipb, BUFSIZ, stdin);
                t = atoi(ipb);
                switch (t)
                {
                case 0:
                        exit(0);
                case 16:
                case 25: pAnswer = pAlphabet;    break;
                case 36: pAnswer = pInscription; break;
                case 49: pAnswer = pValleyMap;   break;
                default: pAnswer = NULL;
                }
                if (NULL != pAnswer)
                {
                        t = (char)pow(t, 0.5);
                        break;
                }
        }
        strInitMatrix(&mtxPuzzle, t, t, sizeof(char));
        memcpy(mtxPuzzle.arrz.pdata, pAnswer, mtxPuzzle.ln * mtxPuzzle.col);
        strShuffleArrayZ(&mtxPuzzle.arrz, &t, sizeof(char), (unsigned int) time(NULL));
        PrintMatrixInMat(&mtxPuzzle);
        do
        {
Lbl_Resume:
                fflush(stdin);
                memset(ipb, 0, BUFSIZ);
                printf("Location h/?> ");
                fgets(ipb, BUFSIZ, stdin);
                if (1 == CommandParser(&mtxPuzzle, ipb))
                {
                        int i;
                        do
                        {        /* Query users to ensure their choices correct. */
                                printf("?Are you sure to exit?Y/n/r?> ");
                                i = strlen(fgets(ipb, BUFSIZ, stdin));
                                if (i > 1)
                                        i -= 2;
                                ipb = tolower(ipb);
                                if ('r' == ipb)
                                        goto Lbl_Restart;
                                if ('n' == ipb)
                                {
                                        printf("Resume...\n");
                                        PrintMatrixInMat(&mtxPuzzle);
                                        goto Lbl_Resume;
                                }
                                putc('\n', stdin);
                        }
                        while (ipb != 'y');
                        stkFreeL(&stkAction);
                        strFreeMatrix(&mtxPuzzle);
                        return 0;
                }
        }
        while (TRUE != ValidateMatrix(&mtxPuzzle));
Lbl_Restart:
        stkFreeL(&stkAction);
        strFreeMatrix(&mtxPuzzle);
goto Lbl_Again;
        return 0;
}


Welcome to Puzzle at StoneValley 1.1.7.9.
      Please input:
      16: playing a 4*4 puzzle. (Alphabet)
      25: playing a 5*5 puzzle. (Alphabet)
      36: playing a 6*6 puzzle. (Inscription)
      49: playing a 7*7 puzzle. (Map)
         0: Exit.
?> 16
abcdMove: 0.
+----+
a|JAFD| ABCD
b|EIBN| EFGH
c| OKM| IJKL
d|HGCL| MNO
+----+
Location h/?> aaad
abcdMove: 2.
+----+
a|AFD | ABCD
b|JIBN| EFGH
c|EOKM| IJKL
d|HGCL| MNO
+----+
Location h/?> ?
      Input 'xy' to hit a square in the puzzle,
      whereas x denotes line, y denotes column.
      Input 'z' to undo.
      Input '0' or 'q' to quit.
      Input 'h' or '?' for help.
      A sequence of actions is allowed to input, such that 'aadaq?dd'.
abcdMove: 2.
+----+
a|AFD | ABCD
b|JIBN| EFGH
c|EOKM| IJKL
d|HGCL| MNO
+----+
Location h/?>
玩法如上所示,输入要点击的方块的坐标来移动方块,将方块完全排序完成后游戏成功。


static/image/hrline/4.gif


以下是一段关于矩阵用法的程序,它能帮助读者更进一步了解StoneValley里的矩阵:
#include <stdio.h>
#include <string.h>
#include "../src/svstring.h"

// Function: Plus
// Desc:   Addition.
//         Please refer to the definition of callback function CBF_ALGEBRA.
// Return:   CBF_CONTINUE only.
int Plus(const void * pa, const void * pb)
{
        *(char *)pa += *(char *)pb;
        return CBF_CONTINUE;
}

// Function: Minus
// Desc:   Subtraction.
//         Please refer to the definition of callback function CBF_ALGEBRA.
// Return:   CBF_CONTINUE only.
int Minus(const void * pa, const void * pb)
{
        *(char *)pa -= *(char *)pb;
        return CBF_CONTINUE;
}

// Function: Times
// Desc:   Multiplication.
//         Please refer to the definition of callback function CBF_ALGEBRA.
// Return:   CBF_CONTINUE only.
int Times(const void * pa, const void * pb)
{
        *(char *)pa *= *(char *)pb;
        return CBF_CONTINUE;
}

// Function: PrintMatrix
// Desc:   Print a matrix.
// Param:    pmat: pointer to a matrix you want to print. width: Print matrix in a width * width area.
// Return:   N/A.
void PrintMatrix(P_MATRIX pmat, const char width)
{
        char i, j, k, b, m, n;
        for (i = 0; i < width; ++i)
        {
                for (j = 0; j < width; ++j)
                {
                        for (k = 0, b = FALSE; k < (const char) pmat->col; ++k)
                        {
                                if (NULL != strGetValueMatrix(&m, pmat, 0, k, sizeof(char)) && m == i &&
                                        NULL != strGetValueMatrix(&n, pmat, 1, k, sizeof(char)) && n == j
                                        ) b = TRUE;
                        }
                        if (b)
                                printf("E");
                        else
                                printf(" ");
                }
                printf("\n");
        }
}

// Function: main
// Desc:   Program entry.
// Param:    N/A.
// Return:   0: no error. NOT 0: allocation failure.
int main(void)
{
        const char data[] = { // Stores a big letter E.
        //    123456789 10 11 12 13 14 15 16
        /*1*/ 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, // Y.
        /*2*/ 0, 1, 2, 3, 4, 0, 0, 1, 2, 3, 0, 0, 1, 2, 3, 4// X.
        };
        const char conv[] = { -1, 0, 0, 1 };
        char c = 5;
        MATRIX mat; // A matrix header.
        P_MATRIX pmword, pmimage, pmtemp, pmconv, ppmat; // Matrix pointers.
        CBF_ALGEBRA cbfalg;
        if (NULL == (pmword= strCreateMatrix(2, 16, sizeof(char)))) {
                c = 4; goto Lbl_Clr;
        }
        if (NULL == (pmimage = strCreateMatrix(2, 16, sizeof(char)))) {
                c = 3; goto Lbl_Clr;
        }
        if (NULL == (pmtemp= strCreateMatrix(2, 16, sizeof(char)))) {
                c = 2; goto Lbl_Clr;
        }
        if (NULL == (pmconv= strCreateMatrix(2,2, sizeof(char)))) {
                c = 1; goto Lbl_Clr;
        }
        // Assemble cbfalg array for strM3Matrix.
        cbfalg = Plus;
        cbfalg = Times;
        // Assemble ppmat for strM3Matrix.
        ppmat = pmtemp; // Matrix C.
        ppmat = pmconv; // Matrix A.
        ppmat = pmword; // Matrix B.
        // Copy original data.
        memcpy(pmword->arrz.pdata, data, sizeof(data));
        memcpy(pmconv->arrz.pdata, conv, sizeof(conv));
        // Fill pmimage with x=5, y=5.
        strSetMatrix(pmimage, &c, sizeof(char));
        // Print big letter 'E'.
        PrintMatrix(pmword, 5);
        printf("\nAfter converting:\n\n");
        strM3Matrix(ppmat, &c, sizeof(char), cbfalg); // Flip.
        strM2Matrix(pmimage, pmtemp, sizeof(char), Minus); // Move right down.
        // Assemble matrix header. Subtract every y by 5.
        mat.arrz.pdata = pmimage->arrz.pdata;
        mat.arrz.num = pmimage->arrz.num;
        mat.ln = 1;
        mat.col = pmimage->col;
        c = 5; strM1Matrix(&mat, &c, sizeof(char), Minus); // Move up.
        PrintMatrix(pmimage, 6); // Print converted 'E'mage.
        c = 0;
Lbl_Clr:
        switch(c)
        {
        case 0: strDeleteMatrix(pmconv);
        case 1: strDeleteMatrix(pmtemp);
        case 2: strDeleteMatrix(pmimage);
        case 3: strDeleteMatrix(pmword);
        default: break;
        }
        return (int)c;
}

0xAA55 发表于 2020-11-2 22:10:41

不喜欢StoneValley的代码风格,如果是我去用,我可能会写一个针对代码风格的Wrapper

usr 发表于 2020-11-2 22:30:57

0xAA55 发表于 2020-11-2 22:10
不喜欢StoneValley的代码风格,如果是我去用,我可能会写一个针对代码风格的Wrapper ...

在/Samples/下提供了一个lex文件,可以用来转换命名方式
页: [1]
查看完整版本: 【C】基于控制台的拼图小游戏