蛙人冠军


11

游戏

我们大多数人都知道Frogger,这是80年代的街机游戏,其目标是让青蛙安全地越过繁忙的高速公路和充满危险的池塘,安全地到达家中。

一个挑战是发行几个月前制定一个青蛙的克隆。但是,为什么可以在 Frogger 时克隆 Frogger?:)

考虑以下简化播放网格:

 XXXXXXXXXXXXXXXXXXXXXXX  North Safe Zone
 -----------------------
|                       | <<<<       Express Lane West        (Lane 1)
|                       |     >      Gridlock East            (Lane 2)
|                       |   <<       Freeflowing Traffic West (Lane 3)
|                       |    <       Gridlock West            (Lane 4)
|                       |     >>>>   Express Lane East        (Lane 5)
 -----------------------
 XXXXXXXXXXX@XXXXXXXXXXX  South Safe Zone
 \__________ __________/
            '
  23 cells horizontally

我们有5条车道,每条车道宽23个,还有两个安全区(青蛙可以左右安全移动),也有23个车宽。您可能会忽略左右边框,因为它们是为了图形清晰。

我们的青蛙始于南部安全区的中心(第12个单元格),如@上图中的a 所示。

游戏中的时间分为称为帧的离散步骤。Froggy是一种快速青蛙,可以每帧在任何方向(上,下,右,左)跳一个单元格。他还可以选择对任何帧保持静止。五个车道上的交通以恒定速度移动,如下所示:

  • 快速车道西(车道1)的交通量每帧向左移动2个像元
  • 死锁东车道(车道2)中的交通每第二帧向右移动1个像元
  • 自由流动的西行车道(3道)上的交通每帧向左移动1个像元
  • 死锁西车道(第4车道)的交通每第二帧向左移动1个像元
  • 快速车道东(车道5)的交通量每帧移动2个像元

流量本身被唯一定义为大约。此文本文件中有 3,000个时间步。“交通”由车辆和车辆之间的空间组成。任何非空格的字符都是车辆的一部分。文本文件包含五行,分别对应于五个车道(顺序相同)。

对于西行车道,在第0帧开始时(比赛开始),我们认为车道中的第一辆车刚好在比赛网格的右边缘上方。

对于东行车道,应将交通字符串视为“向后”,因为车辆应从字符串的末端开始出现。在第0帧开始时,我们认为这些车道中的第一辆车刚好超出比赛场地的左边缘。

考虑一个例子:

Traffic Lane 1:  [|==|  =
Traffic Lane 2:  |) =  o
Traffic Lane 3:  (|[]-[]:
Traffic Lane 4:  <| (oo|
Traffic Lane 5:  |==|] :=)

然后,播放网格将如下所示:

Start of Frame 0       XXXXXXXXXXXXXXXXXXXXXXX
                                              [|==|  =
                |) =  o
                                              (|[]-[]:
                                              <| (oo|
              |==|] :=)
                       XXXXXXXXXXXXXXXXXXXXXXX

Start of Frame 1       XXXXXXXXXXXXXXXXXXXXXXX
                                            [|==|  =
                |) =  o
                                             (|[]-[]:
                                              <| (oo|
                |==|] :=)
                       XXXXXXXXXXXXXXXXXXXXXXX

Start of Frame 2       XXXXXXXXXXXXXXXXXXXXXXX
                                          [|==|  =
                 |) =  o
                                            (|[]-[]:
                                             <| (oo|
                  |==|] :=)
                       XXXXXXXXXXXXXXXXXXXXXXX

Start of Frame 3       XXXXXXXXXXXXXXXXXXXXXXX
                                        [|==|  =
                 |) =  o
                                           (|[]-[]:
                                             <| (oo|
                    |==|] :=)
                       XXXXXXXXXXXXXXXXXXXXXXX

在通道中的所有流量都“耗尽”(即字符串用完)之后,我们认为字符串中的所有字符均为空格。

如果发生以下任何情况,我们的青蛙将被压扁

  • 在任何框架上,青蛙都会占据车辆所占据的牢房
  • 青蛙在快速车道上保持静止,并且在该帧中有1个单元格宽度的车辆经过他
  • 青蛙“通过”向西行驶的车辆向东跳跃,或通过向东的行驶车辆向西跳跃
  • 青蛙在任何帧上都跳出7(线)乘23(单元)的网格

请注意,这是将青蛙压扁的唯一条件。特别地,允许青蛙沿着“伴随”交通跳来跳去,就像青蛙跳入或跳出在同一车架中被宽度为1的车辆通过的快车道中的单元格一样。

目标和计分

编程挑战的目的是在最后一辆车离开游戏场之前使青蛙越过道路越多。即,程序在第X帧完成后立即终止,其中X帧是第一个将网格带到没有更多车辆的状态的帧。

程序的输出应为字符串(或文本文件),其中包含使用以下编码的青蛙的移动顺序:

<   frog moves left
>   frog moves right
^   frog moves up
v   frog moves down
.   frog remains stationary

例如,字符串<<^.^表示青蛙向左移动两次,然后向上移动,然后暂停一帧,然后再次向上移动。

每当青蛙从南部安全区越过到北部安全区时,就得分一分,并且青蛙从北部安全区越过到南部安全区时得分一分。

一些重要的规则:

  1. 禁止挤压青蛙。
  2. 请内联或以文本文件(例如,使用pastebin.com)将解决方案(移动顺序)与程序代码一起发布。
  3. 我们的青蛙是有先见之明的,因此您的程序可以在寻找解决方案的同时使用任何框架中的所有流量数据。这包括尚未到达播放网格的流量数据。
  4. 网格不环绕。退出网格将导致青蛙被压扁,因此是不允许的。
  5. 流量绝对不会“重置”或青蛙“遥不可及”。模拟是连续的。
  6. 青蛙离开后可能会返回南部安全区,但这不算为一点。对于北部安全区也是如此。
  7. 比赛获胜者是产生移动顺序的程序,该移动顺序产生的交叉次数最多。
  8. 如有其他疑问或疑虑,请随时在评论部分提问。

为了提供一些额外的激励,我会在获胜的程序中增加+100 rep的赏金。

奖金

青蛙触碰到的游戏网格的每个角点时,其基本得分为+ 2.5%(最高+ 10%)。网格的四个角是两个安全区域的最左侧和最右侧的单元。

如果您的移动顺序使青蛙在整个模拟过程中都保持在青蛙起始格的左侧或右侧+/- 4个单元之内,则+ 25%的基本得分(当然,他可以自由地垂直移动)。

没有计分奖励,但是OP中的特殊道具将发给发布快速n'n脏解决方案验证程序的任何人,因此我不必编写程序。;)验证者只需接受一系列举动,确保其合法性(根据规则和交通量文件),并报告其得分(即过境总数)。

*总分数等于基本分数加奖金,四舍五入至最接近的整数。

祝你好运青蛙!


对于任何想要发布解决方案验证器的人:请不要将其发布为答案
Geobits 2014年

确实。注释中指向代码的链接或作为解决方案的附录,将是提交验证器的首选方法。
COTO 2014年

慢车道前进的第一帧应该与其他车道前进的第一帧相同还是晚一帧?
feersum

另外,是否有可能在所有赛车都离开场地的第一帧到达终点区而得分?
feersum

@feersum:慢车道后移一帧。如示例所示,它们在第一帧中保持静止。至于最后一帧的青蛙得分:是的,这是可能的。如果青蛙已经移动到最后一辆车离开比赛场地的同一帧中的安全区域,则得分为1分。
COTO 2014年

Answers:


5

C ++:176

输出:

176
^^^^^^>vvvvvv>^^^^>^^>>>>>>><<<.vv>v<<<.vvv<<<<<<^.^.^.^.>.>^^<.>>>>>>v>v>.>v<<<<v.<.vv<<<<<^^.<^<<<<<<^.^^>>>>vv>.>.v<v.vv>>^>>^<^<<<<<<<^>.>^^<<<.>>>>>vv>v<vvv<<<<<^^.<^^^^.....vv>.>.>.>v<<vvv<<>>>>^>^<.<.^^>^^>>>>vv.>v<vv.v^>^<.<.<^<^>.>^^>>>>vv.v<<<<<<.vvv<<<<^^^<<v^^.>.^^..>>>>>>vv>.>.v.vvv>>>^>^^<<<<<<<<<^>.>^^>>>>vvv<<v.<.vv<<<<<<>>>>>^^^<<<^^^<<>>vv>.v<<>vvv<<><>>>>>>>^>^^<^^^<.>>>>>>>>vv>.>v<<<vvv<<<<<^^^<<v.<.<^<<^.^<<^...>>>>>>>>>>>>>vv>v<vvv<<<<<<<<^>^.<.^^>.>^^<<<<<<<..>>>>vv>.vvvv<<<<>^.v>>>^^.^^>^<^<>>>>>>>>>vv>vvvv<<<<^^^<^>^<<^.>>vv>v<vvv<<<<<<<>>>>>>>^>^^^.^^>>>>>vvv<<vvv<<<^^^^^^<vvv<<<<<<<vvv<<<>>>>>>>>^^.<.<.^.^.^<^<>>>>>>>>>>vvv<<.vvv<<<^^<^<<^^<<^<<<>>>>vv>.vvvv>>^>>^<.<.<^<<<<<.^^<<^.......>vv.>.>.>v<vvv>>>>>>^>^.<.<^<.^^^<.>>>>>>>vvv<<<v.<vv<<<^^<.<.<.<^<<<^>^^..........v<vvvv>v>>>>>^>>^^^>^^>>>vv>v<vvv<<<<<<<<>^^.<.<^<^^^<.........>vv.>.vvvv>>>>^^<.<.<^^^<^<<.v<v>.>.>.>.>.>.>.vvv.v<<<<<^^<^<^>.>.>.>.^<^.>>>>>>>>vv>v<<vvv<<>>>>>^^^.^.>^<^<<<<<<<<.vv.>.v<<<.vvv<>>>>>^>^.<.<.<.<^^.^<<^<.....>>vvv<.>>vvv<<<>>>>>>>>^^^<<<.^.>.>^^.>>>>>vv.>v<vvv<<<>>>>>>^>^<^<<^.^^<vvv<<<<<vv.v<>>>>>>>>>>^>^.^^>^^<<<<.>vv.>.vvvv>^>>^.<.<.<^<<<^>^^>>>vv>v<<<<<vvv<<<>>>>>^^<.<.<.<.<^<^>.^^.>>>>>vv>v<>vvv<<<<<<<^>^.<^<<<<<<<^^^.>>>>>vv>v<>vvv>>>^^<.<^<<<^>^^.>>>>vv>.v<<vvv<<<<<^^^<<<<<^>.^^<>>>>>>>vv>.>v<<vvv<<<<<<<>>>^>>.>^^^.>^^<>>>>>>vv>vv.<.<.vv>>>>^^<.<.<.<^<<^.>^^.>>>>>vv.>.>v<<vvv<<>>>>>^^<.<.<.^^>.>.>^^<<<<<<<<.>>>>>>vv>v<<v.<.<vv<<<<^^.<^<<<.^^^<.vv.>v<vvv<<<>>>>>>^>^<^<<^.^<^<<.>>vv>.>.>.>vvvv>>>>>>^>^^^^^<<<.vvv<<<<<<vvv>>>>>^>^<.<^<<^.>.>.^^>>>>vv>.>v<<<<<<vvv<<<<^^^<.^.>^^>>>>vvv<<v.vv<<<^>>^^<<<.^^<<^>>>>>>>>>vvv.v.vv<>>>>>^>^^<<<<<<<<<<<<<<<<^^^..>>>>>vv>.>.>.>v<<v.<.<.vv<<<<<^^^<^>.^^...>>>>>>>>vv.v<.vvv>>>^>^<.<^^^^>>>vv>v<<<vvv<<<<>>>>>>^>^.<.<^<^>.>^^.>>>>>vv.v<<<<<<<<vvv<<<<^^<^<<<<<><^>.>^^>>>>vv.>.>.vvv.v>>>>>>>^^<.<^<<^^^>>>vv>.>.>.>.>v<<v.<.<vv>>>>^^^<<<<<.^.>^^>>>vv>.>v<vvv<<<<^^<.<.<.^^>^^>>>vv>.>.v<<<v.<vv<<<<^^.<^<<<^^^>>>>vvvvvv<<<<<<<<>^^.^>>^^^<>>>>>>>vvv<<<v.vv>>>>>^>>^^<<<<<^>.^^>>>>vv>.>v<<<<vvv<<<^^<.<.<.<^<<<<<^>^^>>>>vv.>vv.v.v<^>.>^.<.^^^^>>>>vv>.>.>.v<<<<<<vvv>>>>^^<.<.<^<<^^<^>>>>>>vv>v<<.vvv^>^^<<<^>^^<<<<<<.vvv<<vv>.v>>>>>>^>.>^^<<<<<^>.>.>.>.>.^^>>>>vv>.>.>v<<<<<vvv<<<<^^<^<<^.^^>>>>vv>.>.>.>v<vvv<<<<<^^.<.<^<<^^^>>>>>>>>>>>vv>.>.>.>.>vvvv<<<<^^.<.<^<^^^<<<<<<vvv<v.<vv<<<<^v>>>>>>>>>>^^^<.^.>.>.^^>>>>vvv<<<<<<<vvv<<<<<<<>^^.<^<.^^^.>>>>vv>v<vvv<>>>>^^.<.^.^.>.^<^>>>>>>>>vvvv.<.<vv<<<<^^^<<<<<^>.^^<.vv>.v<<vvv<<><>>>>>^>>^.<^<^^^<<<.>>vv>.>v<<<<v.vv>^>>^.<^^.^^.>>>>>vv>.>.>.v<v.<vv<<<<<^.^^^.>^^<>>>>>>vv>.v<<<v.<vv<<<<>>>>>>>>>>>^^<.<.<.<.<^<<^^<^<<<>>>>>>>vv>v<<<vv.v>>>>>>>>^>^<.<^<<<<<<<<<<<.^.>^^<<<vvv.v.<vv<<<^.>>^.<^^.>.>^^<<......vv.v>vvv>>>^.v^>>^^<<^^^<.>>>>>>>vvv<v.<.<.vv<<<<^^<.<.<.^^^^<.>>>>>>>>vvv<v.<vv^.>^^<<^^^vv>vvvv^>>>>>>>^^^^^vvvvvv^^^^^^vvvvvv>>>>

位置X时间X已访问拐角集的状态组合少于800万,因此可以在不到1秒的时间内彻底搜索它们。如果代码没有错误,那么就不可能被击败。最佳策略是使用整个木板,因为这可使蛙式游戏横穿马路160次,而局限于中央时大约为120次。由于交通十分繁忙,前往拐角处无需花费任何费用。

/* feersum  2014/9
 /codegolf/37975/frogger-champion */

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <cstring>

#define B(F, CST, X, Y) best[ ((F)*NCST + (CST)) * BS + (Xmax-Xmin+1) * ((Y)-Ymin) + (X)-Xmin]
#define TL(i) ((int)t[i].length())
#define ABSPEED(I) (speed[i]<0?-speed[i]:speed[i])
#define errr(X) { cout<<"ERROR!!!!!!!!"; return X; }

#define DRAW 0
#if DRAW
    #include <unistd.h>
#endif


using namespace std;

int bc(int cs) {
    int c = 0;
    while(cs) {
        c += cs&1;
        cs >>= 1;
    }
    return c;
}

int main()
{
    const bool bonusTwentyfive = false;
    const int COLS = 23, T_ROWS = 5, YGS = T_ROWS + 2, XGS = COLS + 2;
    string t[5];
    int speed[] = {-4, 1, -2, -1, 4};
    ifstream f("t.txt");
    for(int i = 0; i < T_ROWS; i++)
        getline(f, t[i]);
    if(f.fail())
        return 1;
    f.close();
//for(int i = 0; i < 5; i ++)t[i]="xxx";

    char g[XGS][YGS];

    int mov[][3] = { {-1,  0, '<'},
                     {+1,  0, '>'},
                     { 0, -1, '^'},
                     { 0, +1, 'v'},
                     { 0,  0, '.'} };


    int Ymin = 0, Ymax = YGS-1;


    const int Xstart = 11, Xmaxmove = bonusTwentyfive ? 4 : 10;
    const int Xmin = Xstart - Xmaxmove, Xmax = Xstart + Xmaxmove;

    const int NCST = bonusTwentyfive ? 1 : 1<<4;

    int maxfr = 0;
    for(int i = 0; i < T_ROWS; i++) {
        if(speed[i] < 0) {
            for(int m = 0, n = t[i].length()-1; m < n; ++m,--n)
                swap(t[i][m], t[i][n]);
        }
        int carslen = TL(i);
        for(char*c = &t[i][speed[i]>0?TL(i)-1:0]; carslen; carslen--, speed[i]>0?--c:++c)
            if(*c != ' ')
                break;
        if(carslen)
            maxfr = max(maxfr, ( (carslen + COLS) * 2 + ABSPEED(i)-1 ) / ABSPEED(i));
    }
    const int BS = (Xmax-Xmin+1)*(Ymax-Ymin+1);
    int *best = new int[(maxfr+1)*NCST*BS];
    memset(best, 0xFF, (maxfr+1)*NCST*BS*sizeof(int));
    memset(g, ' ', sizeof(g));
    B(0, 0, Xstart, Ymax) = 0;

    for(int fr = 0; fr < maxfr; fr++) {
        for(int r = 0; r < T_ROWS; r++) {
            for(int x = 0; x < XGS; x++) {
                int ind = speed[r] > 0 ? TL(r)-1 + x - fr*speed[r]/2 : x - (XGS-1) - fr*speed[r]/2;
                g[x][r+1] = ind >= 0 && ind < TL(r) ? t[r][ind] : ' ';
            }
        }
        for(int x = Xmin; x <= Xmax; x++) {
            for(int y = Ymin; y <= Ymax; y++) {
                if(g[x][y] != ' ')
                    continue;
                for(unsigned m = 0; m < sizeof(mov)/sizeof(mov[0]); m++) {
                    int X = x + mov[m][0], Y = y + mov[m][1];
                    if(X < Xmin || X > Xmax || Y < Ymin || Y > Ymax)
                        continue;
                    if(!(mov[m][0]|mov[m][1]) && y > 0 && y > T_ROWS && g[x][y-speed[y-1]/4] != ' ')
                        continue;

                    int csor = ((X==Xmin|X==Xmax)&(Y==Ymin|Y==Ymax)&!bonusTwentyfive) << ((X==Xmax) + 2*(Y==Ymax));

                    for(int cs = 0; cs < NCST; cs++) {
                        int N = B(fr, cs, x, y);
                        if(!~N)
                            continue;
                        if((!(N&1) && y == 1 && Y == 0) ||
                              (N&1 && y == T_ROWS && Y == T_ROWS+1))
                            ++N;
                        if(N > B(fr+1, cs|csor, X, Y))
                            B(fr+1, cs|csor, X, Y) = N;
                    }


                }
            }
        }
    }

    int score = 0, xb, yb, cb, nb;
    for(int x = Xmin; x <= Xmax; x++) {
        for(int y = Ymin; y <= Ymax; y++) {
            for(int cs = 0; cs < NCST; cs++) {
                if(B(maxfr, cs, x, y) * (40 + bc(cs)) / 40 >= score) {
                    score = B(maxfr, cs, x, y) * (40 + bc(cs)) / 40;
                    xb = x, yb = y, cb = cs, nb = B(maxfr, cs, x, y);
                }
            }
        }
    }
    char *mvs = new char[maxfr+1];
    mvs[maxfr] = 0;

    for(int fr = maxfr-1; fr >= 0; --fr) {
        for(int r = 0; r < T_ROWS; r++) {
            for(int x = 0; x < XGS; x++) {
                int ind = speed[r] > 0 ? TL(r)-1 + x - fr*speed[r]/2 : x - (XGS-1) - fr*speed[r]/2;
                g[x][r+1] = ind >= 0 && ind < TL(r) ? t[r][ind] : ' ';
            }
        }
        for(int x = Xmin; x <= Xmax; x++) {
            for(int y = Ymin; y <= Ymax; y++) {
                if(g[x][y] != ' ')
                    continue;

                for(unsigned m = 0; m < sizeof(mov)/sizeof(mov[0]); m++) {
                    int X = x + mov[m][0], Y = y + mov[m][1];
                    if(X < Xmin || X > Xmax || Y < Ymin || Y > Ymax)
                        continue;
                    if(!(mov[m][0]|mov[m][1]) && y > 0 && y > T_ROWS && g[x][y-speed[y-1]/4] != ' ')
                        continue;

                    int csor = ((X==Xmin|X==Xmax)&(Y==Ymin|Y==Ymax)&!bonusTwentyfive) << ((X==Xmax) + 2*(Y==Ymax));

                    for(int cs = 0; cs < NCST; cs++) {
                        int N = B(fr, cs, x, y);
                        if(!~N)
                            continue;
                        if((!(N&1) && y == 1 && Y == 0) ||
                              (N&1 && y == T_ROWS && Y == T_ROWS+1))
                            ++N;
                        if(X==xb && Y==yb && N == nb && (cs|csor) == cb) {
                            mvs[fr] = mov[m][2];
                            xb = x, yb = y, nb = B(fr, cs, x, y), cb = cs;
                            goto fr_next;
                        }
                    }
                }
            }
        }
        errr(3623);
        fr_next:;
    }

    if((xb-Xstart)|(yb-Ymax)|nb)
        errr(789);
    #if DRAW

        for(int fr = 0; fr <= maxfr; ++fr) {
            memset(g, ' ', sizeof(g));
            for(int r = 0; r < T_ROWS; r++) {
                for(int x = 0; x < XGS; x++) {
                    int ind = speed[r] > 0 ? TL(r)-1 + x - fr*speed[r]/2 : x - (XGS-1) - fr*speed[r]/2;
                    ind >= 0 && ind < TL(r) ? g[x][r+1] = t[r][ind] : ' ';
                }
            }
            g[xb][yb] = 'F';
            for(int y = 0; y < YGS; y++) {
                for(int x = 0; x < XGS; x++)
                    cout<<g[x][y];
                cout<<endl;
            }
            cout<<string(XGS,'-')<<endl;
            usleep(55*1000);
            for(int i = 0; i < 5; i++) {
                if(mvs[fr] == mov[i][2]) {
                    xb += mov[i][0];
                    yb += mov[i][1];
                    break;
                }
            }
        }

    #endif
    cout<<score<<endl;
    cout<<mvs<<endl;
}

1
我不确定您如何定义“状态”。如果我们将系统状态视为青蛙运动的时间轮廓,则大约有5 ^ 3000个状态,对应于5 ^ 3000个可能的输入(每〜3000帧五个可能的输入)的运动轮廓。显然,其中只有一小部分是可以接受的,但是可以搜索数百个数量级的可允许状态数。提交最终答案时,请同时提供动作清单。
COTO 2014年

另外,如果不清楚,请注意,只要不违反任何“挤压”规则,青蛙就可以在行进中左右跳动。您不仅限于等待青蛙能够在没有横向运动的情况下垂直跳跃的窗户。
COTO 2014年

@COTO在计算“状态”时,我确实忘记了一件重要的事情,即到目前为止的穿越次数,因此我试图给出更清晰的解释。规范写得很好。看来我第一次正确地解释了所有这些特殊问题。
feersum

我计算出的最佳值为162 + 10%= 178个交叉,但您的距离足够近。我真的不希望这证明是蛮力的,但是显然我应该多考虑这个问题。公平地说,我将奖励您100代表。
COTO 2014年

显然,我必须等待24小时才能授予“赏金”。由于什么原因:谁知道。SE一定不希望按时奖励答案。如果我们这样做,恐怖分子将获胜。:O
COTO 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.