有没有更快的方法来检测游戏功能?

时间:2013-09-15 00:45:28

标签: c algorithm

我有一个游戏。屏幕上将随机显示5种符号。如果屏幕上有3个或更多相同的符号,则玩家可以赢得一些钱。

例如,如果我有符号ABCDEF

F可以替代任何其他类型的符号(通配符)。

因此,当屏幕上显示AAABCAFABB时,就意味着我赢得了3-A次胜利。 如果屏幕上显示AAFBB,则表示我赢得了3-A3-B两种胜利。

现在我有一个关于屏幕上显示内容的数组;我需要找到足够快的方式来检测游戏结果。

我现在有这个方法:

我为每个符号提供了一个代码:A -> 1B -> 2C -> 3D -> 4E -> 5F -> 0xF

就像我有一个数组:{A,B,C,D,E}我将它们转换为屏幕代码:0x12345。

然后我列出了获奖面具:

0x11111
0x22222
0x33333
0x44444
0x55555
0x11110
0x01111
0x22220
...
0x00111
0x01110
0x11100

我使用屏幕代码,对每个掩码代码执行&,例如0x111f1 & 0x11111 == 0x11111,然后我知道我有5-A胜利。

是否还有其他任何技巧可以做到这一点?

6 个答案:

答案 0 :(得分:5)

正如ilent2所说,您当前使用的方法实际上不起作用,因为您使用的符号表示不是按位可分的(例如,A = 1B = 2和{ {1}}因C = 3}而中断。

解决这个问题的直接方法是每个位置使用5位,并使用:

A | B = C

然而,通过将每个符号表示为在4位字段中设置的两位,有一种聪明的方法,因为有6种(A = 0x01 (0b00001) B = 0x02 (0b00010) C = 0x04 (0b00100) D = 0x08 (0b01000) E = 0x10 (0b10000) F = 0x1F (0b11111) )种可能的组合:

= 4 x 3 ÷ 2

这样,任何两个符号的按位AND或OR都不是另一个有效符号。

就检测匹配而言,您已经使用的方法是在正确的轨道上。利用位移来检测多个位置的匹配,而不是为每个组合分别设置模式。

答案 1 :(得分:2)

这是我相当快的代码。

它输入一个包含5个字符的数组(例如:"AAFBB"),并跟踪它找到的连续字母数。只要连续计数为3或更大,它就会增加该字母的Win计数器。

它最终返回一个包含5个整数的数组,表示每个整数的胜利。 因此,对于输入"AAFBB",输出为{1, 1, 0, 0, 0},表示A和B都“赢了”一次。

(调用者负责释放返回的数组)

int* checkWins(char input[5])
{
    int  Counts[5]= {0};
    int* Wins = (int*)calloc(5, sizeof(int));

    char prevSymbol=input[0];

    for(int i=0; i<5; ++i)
    {
        if (input[i] == 'F')
        {
            // WildCard!  Increment ALL counters
            for(int j=0; j<5; ++j)
            {
                Counts[j]++;
                if (Counts[j] >= 3)
                {
                    Wins[j]++;
                }
            }
            prevSymbol = input[i];
            continue;
        }

        if (input[i] == prevSymbol || prevSymbol == 'F')
        {
            Counts[ input[i]-'A']++;
            if (Counts[ input[i]-'A'] >= 3)
            {
                Wins[ input[i]-'A']++;
            }
        }
        else
        {
            Counts[prevSymbol-'A']=0;
        }
        prevSymbol = input[i];
    }
    return Wins;
}

int main(void)
{
    int* wins = checkWins("AAFBB");

    // Check wins[0]... wins[4] for winners.
    // wins[0] and wins[1] should both be 1
    // Indicating AAA and BBB according to the rules.

    free(wins );
}

答案 2 :(得分:1)

可以通过符号串单次确定结果。这段代码可以做到:

#include <assert.h>
#include <stdio.h>

/* You can have 0, 1 or 2 wins */

typedef struct WinInfo
{
    unsigned char streak;
    unsigned char letter;
    unsigned char start;
} WinInfo;
typedef struct Win
{
    int wins;
    WinInfo windata[2];
} Win;

static void add_win(Win *win, int streak, int start, char letter)
{
    assert(win->wins >= 0 && win->wins <= 1);
    win->windata[win->wins].streak = streak;
    win->windata[win->wins].letter = letter;
    win->windata[win->wins].start = start;
    win->wins++;
}

static void print_win(Win *win, const char symbol[5])
{
    assert(win->wins >= 0 && win->wins <= 2);
    if (win->wins == 0)
        printf("No win for [%.5s]\n", symbol);
    else
    {
        for (int i = 0; i < win->wins; i++)
        {
            printf("Win %d: %d-%c starting at %d in [%.5s]\n",
                   i, win->windata[i].streak, win->windata[i].letter,
                   win->windata[i].start, symbol);
        }
    }
}

static Win check_win(const char symbol[5])
{
    int streak = 0;
    int start = -1;
    char letter = 0;
    Win result = { 0 };

    for (int i = 1; i < 5; i++)
    {
        if (symbol[i] == 'F' || symbol[i-1] == 'F' || symbol[i] == symbol[i-1])
        {
            /* Current and prior symbols are the same, or at least one is 'F' */
            if (start == -1)
            {
                streak = 2;
                start  = i-1;
                letter = symbol[i];
                if (letter == 'F')
                    letter = symbol[i-1];
            }
            else if (symbol[i] != 'F' && letter != 'F' && symbol[i] != letter)
            {
                /* End of a streak -- for example: AFB, FAFB, AAB, FFAB, AFFB */
                if (streak >= 3)
                    add_win(&result, streak, start, letter);
                /* Reset start ... */
                if (symbol[i-1] != 'F')
                {
                    streak = 0;
                    start = -1;
                }
                else
                {
                    /* Step back to first 'F' not preceded by another 'F' */
                    int j = i;
                    while (symbol[j-1] == 'F')
                        j--;
                    start = j;
                    streak = i - j + 1;
                    letter = symbol[i];
                }
            }
            else
            {
                if (letter == 'F')
                    letter = symbol[i];
                streak++;
            }
        }
        else
        {
            /* Mismatch between current and prior symbol */
            if (streak >= 3)
                add_win(&result, streak, start, letter);
            streak = 0;
            start = -1;
        }
    }
    if (streak >= 3)
        add_win(&result, streak, start, letter);
    return result;
}

#include <time.h>
#include <stdlib.h>

int main(void)
{
    const struct test
    {
        char *symbols;
        Win   win;
    } tests[] =
    {
        /* W2WB - wall-to-wall braces */
        { "AAAAA", { 1, { { 5, 'A', 0 }, { 0,   0, 0 } } } },
        { "AAAAB", { 1, { { 4, 'A', 0 }, { 0,   0, 0 } } } },
        { "AAABB", { 1, { { 3, 'A', 0 }, { 0,   0, 0 } } } },
        { "AAABC", { 1, { { 3, 'A', 0 }, { 0,   0, 0 } } } },
        { "AABBC", { 0, { { 0,   0, 0 }, { 0,   0, 0 } } } },
        { "AAFBB", { 2, { { 3, 'A', 0 }, { 3, 'B', 2 } } } },
        { "AAFBC", { 1, { { 3, 'A', 0 }, { 0,   0, 0 } } } },
        { "AAFFB", { 2, { { 4, 'A', 0 }, { 3, 'B', 2 } } } },
        { "ABCDE", { 0, { { 0,   0, 0 }, { 0,   0, 0 } } } },
        { "ABCDF", { 0, { { 0,   0, 0 }, { 0,   0, 0 } } } },
        { "ABCFE", { 0, { { 0,   0, 0 }, { 0,   0, 0 } } } },
        { "AFABB", { 1, { { 3, 'A', 0 }, { 0,   0, 0 } } } },
        { "AFFFB", { 2, { { 4, 'A', 0 }, { 4, 'B', 1 } } } },
        { "AFAFA", { 1, { { 5, 'A', 0 }, { 0,   0, 0 } } } },
        { "AFAFB", { 1, { { 4, 'A', 0 }, { 0,   0, 0 } } } },
        { "AFFFF", { 1, { { 5, 'A', 0 }, { 0,   0, 0 } } } },
        { "BAAAA", { 1, { { 4, 'A', 1 }, { 0,   0, 0 } } } },
        { "BAAAC", { 1, { { 3, 'A', 1 }, { 0,   0, 0 } } } },
        { "BCAAA", { 1, { { 3, 'A', 2 }, { 0,   0, 0 } } } },
        { "FAABB", { 1, { { 3, 'A', 0 }, { 0,   0, 0 } } } },
        { "FFAAB", { 1, { { 4, 'A', 0 }, { 0,   0, 0 } } } },
        { "FFABB", { 1, { { 3, 'A', 0 }, { 0,   0, 0 } } } },
        { "FFFAB", { 1, { { 4, 'A', 0 }, { 0,   0, 0 } } } },
        { "FFFBB", { 1, { { 5, 'B', 0 }, { 0,   0, 0 } } } },
        { "FFFFA", { 1, { { 5, 'A', 0 }, { 0,   0, 0 } } } },
        { "FFFFF", { 1, { { 5, 'F', 0 }, { 0,   0, 0 } } } },
    };
    enum { NUM_TESTS = sizeof(tests)/sizeof(tests[0]) };

    int pass = 0;
    for (size_t i = 0; i < NUM_TESTS; i++)
    {
        Win result = check_win(tests[i].symbols);
        print_win(&result, tests[i].symbols);
        if (result.wins == tests[i].win.wins)
        {
            int fail = 0;
            for (int n = 0; n < result.wins; n++)
            {
                /* Update to record/verify start too */
                if (result.windata[n].streak != tests[i].win.windata[n].streak ||
                    result.windata[n].letter != tests[i].win.windata[n].letter ||
                    result.windata[n].start  != tests[i].win.windata[n].start)
                {
                    printf("!! FAIL !! (wanted %d-%c @%d, actual %d-%c @%d)\n",
                           tests[i].win.windata[n].streak, tests[i].win.windata[n].letter,
                           tests[i].win.windata[n].start, result.windata[n].streak,
                           result.windata[n].letter, result.windata[n].start);
                    fail++;
                }
            }
            if (fail == 0)
                pass++;
        }
        else
            printf("!! FAIL !! (%s: wanted %d, actual %d)\n",
                   tests[i].symbols, tests[i].win.wins, result.wins);
    }
    if (pass == NUM_TESTS)
        printf("== PASS ==\n");
    else
        printf("!! FAIL !! (%d pass, %d fail)\n", pass, NUM_TESTS-pass);

    printf("\nRandom play:\n");
    srand(time(0));
    for (int i = 0; i < 10; i++)
    {
        char symbols[5];
        for (int j = 0; j < 5; j++)
        {
            symbols[j] = rand() % 6 + 'A';
        }
        Win result = check_win(symbols);
        print_win(&result, symbols);
    }

    return (pass != NUM_TESTS); /* 0 success, 1 failure */
}

它具有严格的测试阶段,可确保在特别选择的测试用例中产生正确的结果。它还有一个“随机播放”部分,随机尝试游戏。

示例输出:

Win 0: 5-A starting at 0 in [AAAAA]
Win 0: 4-A starting at 0 in [AAAAB]
Win 0: 3-A starting at 0 in [AAABB]
Win 0: 3-A starting at 0 in [AAABC]
No win for [AABBC]
Win 0: 3-A starting at 0 in [AAFBB]
Win 1: 3-B starting at 2 in [AAFBB]
Win 0: 3-A starting at 0 in [AAFBC]
Win 0: 4-A starting at 0 in [AAFFB]
Win 1: 3-B starting at 2 in [AAFFB]
No win for [ABCDE]
No win for [ABCDF]
No win for [ABCFE]
Win 0: 3-A starting at 0 in [AFABB]
Win 0: 4-A starting at 0 in [AFFFB]
Win 1: 4-B starting at 1 in [AFFFB]
Win 0: 5-A starting at 0 in [AFAFA]
Win 0: 4-A starting at 0 in [AFAFB]
Win 0: 5-A starting at 0 in [AFFFF]
Win 0: 4-A starting at 1 in [BAAAA]
Win 0: 3-A starting at 1 in [BAAAC]
Win 0: 3-A starting at 2 in [BCAAA]
Win 0: 3-A starting at 0 in [FAABB]
Win 0: 4-A starting at 0 in [FFAAB]
Win 0: 3-A starting at 0 in [FFABB]
Win 0: 4-A starting at 0 in [FFFAB]
Win 0: 5-B starting at 0 in [FFFBB]
Win 0: 5-A starting at 0 in [FFFFA]
Win 0: 5-F starting at 0 in [FFFFF]
== PASS ==

Random play:
Win 0: 3-B starting at 1 in [DBBBA]
No win for [DEECC]
No win for [ACAED]
Win 0: 4-D starting at 0 in [DFFFA]
Win 1: 4-A starting at 1 in [DFFFA]
No win for [FADFA]
No win for [CAEAF]
Win 0: 3-C starting at 2 in [AECFF]
No win for [EDAED]
No win for [EDEAC]
Win 0: 3-C starting at 1 in [EFCCA]

此代码版本丢失了导致注释中提到的问题的静态变量。它还将报告结构与结果的打印分开。 check_win()函数不会打印任何内容;那是print_win()的工作。实际上这些变化并不大(但是如果因为名称变更和结构变化而对文件进行区分,它们看起来会很大。

答案 3 :(得分:0)

我假设你将符号放在一个整数数组中,其中0到6的每个整数对应于你的符号,其中1 = A和F = 6.然后你只需循环给定的输入和每个符号检查以下x看你是否看到3的三元组。外循环将运行6次,内循环将运行6 - 最多i次。这将打印出重复项(打印输出AAAABE赢得4A赢和3A赢)但你可以解决这个问题。

    for (int i = 0; i < 6; i++)
    {
        int candidate = symbols[i];
        int j = i;

        while (candidate == symbols[j] || symbols[i] == 6 && j < 6)
        {
            j++;
        }

        if (j - i >= 3)
        {
            // win of type candidate with length of j - i
        }
    }

答案 4 :(得分:0)

您可以按如下方式分配值:A = 10,B = 100,C = 1000,D = 10000,E = 100000,F = 1.我认为enum可以帮助您。 然后你只需要输出输出字符串的值就可以说sum。因此fNumber = sum % 10是您F的号码,aNumber = sum % 10等等。然后只需检查aNumber + fNumber >= 3

答案 5 :(得分:0)

我觉得你建议的面具没什么问题。此外,您根本不需要检测游戏结果:

理论上,每五个字母的胜利都有1/7776的概率发生;每三个字母的胜利,一个105/7776的机会。如果您将随机生成器设置为输出介于1和7776之间的数字,并将每个掩码与这些数字中的一个(或三个字母获胜的情况下的范围)相关联,则程序将“事先知道”是否存在赢或不,以及哪种。

如果没有胜利,则显示随机的无赢洗牌。