Varieties of the game "Bashe". Winning algorithms for the computer

Good evening.
Friends, many of you have probably heard or played the game "Bashe".


The essence of the game, the classic case:

  1. Two people play.
    There are N items on the table, and players take turns taking from 1 to k items.
    The loser is the player who took the last item.

I think now everything is clear, and most people understand what I'm talking about.


For the classic case, and two upgraded ones(about them below), you need to create a move algorithm in which the computer will win the person(who does not know the winning algorithms).

Not standard cases:

  1. Two people play.
    There are N items on the table, players take turns taking any number of items, but no more - "2 * (how much the previous player took)", zero can not be taken. The winner is the player who took the last item.

  1. Two people play.
    There are N items on the table, players take turns taking any number of items, but no more than the previous one took, zero can not be taken.
    The winner is the player who took the last item.

As I said, for each case, you need to create an algorithm in which the computer will win the user.


First, you need to determine the 100% winning positions for each case. The winning position is the position that you leave after your turn, and you win under any circumstances. the opponent's moves.

1 case: All winning positions are in place - (k+1) - > 2(k+1) -> 3(k+1) -> etc.

2 case: All winning positions are fibonacci numbers- 1, 2, 3, 5, 8, 13, 21, 34, etc

3 case: All winning positions are powers of two - 2, 4, 8, 16, 32, etc.


The winning positions were determined. The next (and final) task is to create an algorithm for the computer's progress.

I managed to write a move procedure only for the first case, with "I'm asking for help."

I used pascal, you can write in any language you like.

1 case:

procedure step(var n,k,a:integer);
begin // n - кол-во предметов на столе, k - максимально можем взять, a - наш ход(сколько берем)
if n mod k+1 = 1 then a:=1+random(k)
    else if n mod k+1 = 0 then a:=k
        else a:=(n mod k+1)-1
end;

2 case: no ideas, please help.

3 case:

procedure step(var n,a:integer, var b:integer);
// а - взял предидущий игрок, b - наш ход.
begin
if n mod 2 = 1 then b:=1; //если нам оставили нечетное, начинаем брать по 1 и выигрываем.
    else 
        if a >= n div 2 then b:=n //если предыдущий игрок взял больше половины, забираем всё остальное вместе с последней.
            else ??????????? 
end;

With the third case, the question remains open, my option does not win all the time.

I ask for help in the implementation of the 2nd and 3rd case. Thanks for your attention)

Author: Дух сообщества, 2016-10-21

1 answers

These problems belong to the classical problems on game theory, and many articles have been written on them. The idea of calculating the winning move is based on the theorems of Sprague-Grundy and Bouton. To answer the question "where to go", you need to calculate the game states. Here the game does NOT break up into the sum of the other 2, so it is enough to use recursion. In this case, the state of the game is described by 2 numbers - {количество оставшихся, взято на том ходу}. Let's make a function for example 2 (3 the problem is very muddy), I think that the first move is always 1:

int Val[1000][1000];



int func(int Count, int prevV){
    if (Val[Count][prevV] != -1)
        return Val[Count][prevV];
    char Z[10000];
    memset(Z,0,sizeof(Z));
    for (int i=1;i<=min(2*prevV,Count);i++)
        Z[  func(Count - i,i) ] = 1;
    for (int i=0; ; i++)
        if (!Z[i]){
            Val[Count][prevV] = i;
            return i;
        }

}

int calcStep(int Count, int prevV){
    if (!func(Count,prevV))
        assert(false);
    for (int i=1; ;i++)
        if (!Val[Count - i][i]) return i;
}

int main() {
    for (int i=1;i<1000;i++)
        for (int j=1;j<1000;j++)
            Val[i][j] = -1;
    int count;
    int d;
    int prev;
    prev = 1;
    cin >> count;
    cout <<"Test "<<func(count - 1,1)<<endl;
    while (count){
        cin>>d;
        if (d > 2*prev)
            assert(false);
        count -=d;
        cout << func(count,d)<<endl;
        prev = calcStep(count,d);
        count -= prev;
        cout << count<<" "<<prev << endl;
    }
    return 0;
}

Then we just do all the recursive calculations. Demo program. Link to read http://e-maxx.ru/algo/sprague_grundy

 2
Author: pavel, 2016-10-21 20:17:36