Game theory and sg function

Posted by vijaykoul on Sun, 28 Nov 2021 07:33:01 +0100

Game theory

Define the winning state as the winning state and the losing state as the losing state.

Through reasoning, we can get the following three theorems:

  • Theorem 1: a state without a successor state is a failure state.
  • Theorem 2: a state is a winning state if and only if there is at least one losing state as its successor state.
  • Theorem 3: a state is a must lose state if and only if all its subsequent states are a must win state.

For theorem 1, if the game doesn't go on, the player loses the game.

For theorem 2, if at least one subsequent state of the state is a must fail state, the player can operate to the must fail state; At this time, the opponent's state is bound to fail - the opponent must fail, but on the contrary, he will win.

For Theorem 3, if there is no follow-up state that must be defeated, then in any case, the player can only operate to the winning state; At this time, the opponent's state is a must win state - the opponent must win and lose the game.

Combination game:

  1. Two players operate in turn
  2. The game state set is limited to ensure that the game ends after a limited number of steps
  3. There will be no draw
  4. The successor states of (1, 2, 3) are the first to win, so (1, 2, 3) the first to lose

Rules:

  1. Must lose status < - > all successors are must win status
  2. Must win status < - > at least one successor is a must lose status
  3. A state without a successor is a state of failure

Ferguson games

The time complexity of the solution is very large, and it will not work if the data range is large

1. There are two boxes at the beginning, with m and N sugars respectively.

2. Each movement is to empty one box and take some sugar from the other box to the empty box, so that there is at least one sugar in each of the two boxes.

3. Obviously, the only final state is (1,1).

4. The player who moves last wins, (m,n) is the first to win or lose?

analysis:

1. k=m+n must decrease in each step, and k recurs from small to large (DP)

2. The code outputs all mandatory states with K < 20

3. The states (n,m) and (m,n) are equivalent, and only the case of N < = m is output

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
#define ll long long
#include <map>
using namespace std;
bool dp[50][50] = {0}; // i. j stands for m, N, and the value stands for win or lose 

int main() {
	ios::sync_with_stdio(false);
	dp[1][1] = false;
	for(int k = 3; k < 99; k++) {
		for(int n = 1; n < k; n++) {
			int m = k - n;
			bool &w = dp[n][k - n];
			w = false;
			for(int i = 1; i < n; i++){ // Empty the one on the right and put i one from the one on the left 
				if(!dp[i][n - i]) w = true; // If the next stage is a must lose point, the current stage is a must win point 
			}
			for(int i = 1; i < m; i++) {
				if(!dp[i][m - i]) w = true;
			}
		}
	}
	/*
	for(int i = 1; i < 50; i++) {
		for(int j = 1; j <= i; j++) {
			cout << j << ' ' << i << ' ' << dp[j][i] << endl;
		}
	}*/
	
	return 0;
}

Chomp! game

The chessboard of m * n can take one square at a time and take all the squares on its right and above. The person who gets 1 * 1 in the lower left corner loses

Given m, n, ask Mr. will win or lose? Answer: the first hand will win

Now let's assume that the first hand takes the square (m, n) in the top right corner, and the second hand can take a stone (a, b) to make himself enter the winning situation.

In fact, the first hand can take stones (a, b) at the first time, and then completely imitate the winning steps of the second hand, forcing the second hand to fail.

So there are contradictions. Therefore, there is no winning strategy for the second hand, and there is a winning strategy for the first hand.

Note: this proof is a non constructive existence proof, that is, it only proves the existence of the first hand winning strategy, but does not construct a specific winning strategy.

Although for some special cases, such as the chessboard is square and the chessboard has only two lines, we can find the winning strategy; But for the general situation, no one can give Chomp's general winning strategy.

Divisor game

1 ~ n numbers. Two people take turns to choose a number and erase it and its divisor. The one who erases the last number wins. Ask who will win and try to prove:

Assuming that the backhand will win: then the backhand has a winning strategy. If you take 1 first and then x (the winning point), why don't you take x first and win directly.. 1 is the divisor of any number

Nim games

There are several piles of stones. The number ai of stones in each pile is limited. The legal movement is "select a pile of stones and take away several stones (can't take them)". If it's someone's turn, all the piles of stones have been emptied, it will be judged negative (because he doesn't have any legal movement at the moment).

(a, b, c) is a must fail state < - > A ^ B ^ C = 0, called Nim and

  1. Nim sum of (13, 12, 8): 9, so the first hand wins

How to win? Give your opponent a must lose state. 13 take away 4

Applicable to any heap, all XORs

Division game

n * m matrix. The elements are positive integers of 2 - 10000. You can select one or more integers greater than 1 in one row at a time and turn each of them into a factor. For example, 12 can become 1, 2, 3, 4 and 6. Inoperable input (when all numbers are 1)

analysis:

  1. Each number contains the number of prime factors. Making a number become its factor is equivalent to removing one or more factors
  2. Each row corresponds to a stack of matches, and the prime factor of each number can be regarded as a match

SG function and SG theorem

  1. Any state x, define SG(x) = mex(S), S = {SG(y) | y is the successor state of X}

  2. mex(S) represents the smallest nonnegative integer not within S

    • If x has 5 successor states and SG values are 0, 1, 1, 2 and 4 respectively, then SG(x) = 3,
  3. The SG value of the final state is obviously 0, while other values are derived recursively

  4. SG(x) = 0, X is a must fail state

  5. SG theorem: game and SG function are equal to Nim sum of SG function of each sub game

  6. You can divide and conquer each game and simplify the problem

    • Bouton theorem can be regarded as the direct application of SG theorem in NIM games, because the SG function of single pile Nim games satisfies SG(x) = x

Stone game

n rockfill, a1, a2... an respectively (the size of a is within long long long). Two players operate in turn, selecting a pile at a time and taking at least one stone, but not more than half of the stones. For example, if there are three piles of stones, each pile has 5, 1 and 2 respectively, in the next round, the player can take one or two from the first pile, the second pile can't take, and the third pile can only take one. Whoever can't take the stones will lose.

analysis:

  1. It is different from Nim games, but it can be regarded as the sum of n single pile games

  2. A has a large range and cannot deduce all SG function values according to the definition

  3. Write a recursive program to see if the SG function of a single pile game is regular

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <iostream>
    #include <string>
    #include <algorithm>
    #include <vector>
    #include <set>
    #define ll long long
    #include <map>
    using namespace std;
    int main() {
    	ios::sync_with_stdio(false);
    	cin.tie(0);
    	int sg[100], vis[100];
    	sg[1] = 0;
    	cout << sg[1] << ' ';
    	for(int i = 2; i <= 30; i++) {
    		memset(vis, 0, sizeof(vis));
    		for(int j = 1; (j << 1) <= i; j++) {
    			vis[sg[i - j]] = 1;
    		}
    		for(int j = 0; ; j++) {
    			if(!vis[j]) {
    				sg[i] = j;
    				break;
    			}
    		} 
    		cout << sg[i] << ' ';
    	}
    	return 0;
    }
    

    Print result: 0 1 0 2 1 3 0 4 2 5 1 6 3 7 0 8 4 9 2 10 5 11 1 12 6 13 3 14 7 15

  4. When n is an even number, sg(n) = n / 2, but it seems that there is no law when n is an odd number

  5. When n is an odd number: sg(n) = sg(n / 2)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
#define ll long long
#include <map>
using namespace std;
ll sg(ll x) {
	return x % 2 == 0 ? x / 2 : sg(x / 2); 
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin >> t;
	while(t--) {
		int n;
		ll a, v = 0;
		cin >> n;
		for(int i = 0; i < n; i++) {
			cin >> a;
			v ^= sg(a); // Nim and
		}
		if(v) cout << "YES\n";
		else cout << "No\n";
	}
	return 0;
}

Treblecross game

There are n ≤ 200 grids in a row, some of which have the character x. Two players operate in turn. They can choose a space at a time and put the character x in it. If three consecutive x's appear at this time, the player wins the game. Initially, there will be no three x's in succession. Your task is to judge whether the first player will win or lose. If you win, output all winning strategies.

analysis:

  1. If you have XX or X.X, you must win first

  2. So we don't put x next to an X and next to it

  3. The whole game is divided into several independent segments by x and the "forbidden area" next to it. One segment can be selected for the game at a time. Whoever cannot continue the game will lose. This is the sum of several games

    The mouth on both sides of the mouth is two sub games, and the OX in the middle is a restricted area

  4. g(x) is the sg value of the game corresponding to x grids

  5. g(x) = mex{g(x - 3), g(x - 4), g(x - 5), g(1) ^ g(x - 6), g(2) ^ g(7)...} boundary: g(0) = 0, g(1) = g(2) = g(3) = 1

    1. g(x - 3) correspondingly put x on the leftmost sub, and notice that the leftmost three grids have become restricted areas, leaving X - 3
    2. g(2) ^ g(x - 7) corresponds to the situation in the above. The two sub games on the left and the X - 7 sub games on the right are two independent sub games
  6. Calculate the SG function of the initial situation. Enumerating all policies and subsequent decisions with SG value of 0 is the desired decision

https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=477&page=show_problem&problem=1502

Other situations: there are four restricted areas next to each X (two on the left and two on the right). The restricted areas are represented by i, that is... iiXii... Obviously, if you put it in i, you will lose first

Mark all the restricted areas of the whole picture. The problem becomes to put X in the unmarked area. If you don't put an X, i in iiXii will be marked. The marked grid can't go. Ask the first hand to win / lose

Each unmarked area is a sub game, and sg[i] represents the sg value of the sub game with length I

Pretreatment is enough

The scheme only needs to enumerate where the first step goes and re judge whether the SG function of the situation is 0

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
#define ll long long
#include <map>
#define maxn 200

using namespace std;
int g[maxn + 10];

bool winning(char *state) {
	int n = strlen(state);
	for(int i = 0;  i < n - 2; i++) {
		if(state[i] == 'X' && state[i + 1] == 'X' && state[i + 2] == 'X') return false;
	}
	int no[maxn + 1]; // no[i] = 1 indicates that the grid with subscript 1 is "restricted area", and the distance from "X" is no more than 2
	memset(no, 0, sizeof(no));
	no[n] = 1; // sentry
	for(int i = 0; i < n; i++) if(state[i] == 'X') {
		for(int d = -2; d <= 2; d++) {
			if(i + d >= 0 && i + d < n) {
				if(d != 0 && state[i + d] == 'X') return true; // Two 'X' with a distance of no more than 2 win
				no[i + d] = 1; 
			}
		}
	}
	
	int sg = 0, start = -1; // Starting point of the current sub game
	for(int i = 0;  i <= n; i++) {
		if(start < 0 && !no[i]) start = i;
		if(no[i] && start >= 0) sg ^= g[i - start];
		if(no[i]) start = -1;
	} 
	return sg != 0;
}

int mex(vector<int>& s) {
	if(s.empty()) return 0;
	sort(s.begin(), s.end());
	if(s[0] != 0) return 0;
	for(int i = 1; i < s.size(); i++) {
		if(s[i] > (s[i - 1] + 1)) return s[i - 1] + 1;
	}
	return s[s.size() - 1] + 1;
}
// Preprocessing g array
void init() {
	g[0] = 0;
	g[1] = g[2] = g[3] = 1;
	g[4] = g[5] = 2;
	for(int i = 4; i <= maxn; i++) {
		vector<int> s;
		s.push_back(g[i - 3]); // Put X in the leftmost grid with subscript 0
		s.push_back(g[i - 4]); // Put X in the grid with subscript 1
		if(i >= 5) s.push_back(g[i - 5]); // Put X in the grid with subscript 2
		for(int j = 3;  j < i - 3; j++) {
			if(i - j - 3 >= 0) {
				s.push_back(g[j - 2] ^ g[i - j - 3]); // Sub game
			}
			g[i] = mex(s);
		}
	}
}
int main() {
	ios::sync_with_stdio(false);
	init();
	int T;
	/*
	for(int i = 0; i < maxn; i++) {
		cout << g[i] << ' ';
	}
	*/
	cin >> T;
	while(T--) {
		char state[maxn + 10];
		cin >> state;
		int n = strlen(state);
		if(!winning(state)) cout << "LOSING\n\n";
		else {
			cout << "WINNING\n";
			vector<int> moves;
			for(int i = 0; i < n; i++) if(state[i] == '.') {
					state[i] = 'X';
					if(!winning(state)) moves.push_back(i + 1);
					state[i] = '.';
			}
			cout << moves[0];
			for(int i = 1; i < moves.size(); i++) {
				cout << ' ' << moves[i];
			}
			puts("");
		}
	}
	return 0;
}

Boxgame (uva12293)

  1. Two identical boxes, one with n(n ≤ 10%) balls and the other with 1 ball. Alice and Bob operate in turn.

  2. Empty the box with fewer balls each time (if the number of balls is the same, any one). Take some balls from the other to this. The two boxes are not empty.

  3. The player who cannot operate < = = > (both boxes have only 1 ball) loses. Judge whether the first hand (A) wins or the second hand (B).

Solution:

  1. Because the box with a small number is dumped each time, and then the box with a large number is divided, the rule can be simplified as follows: there are n balls at the beginning, and you can only take no more than n/2 balls each time. The final state is 1 ball, and the player who reaches this state wins.

  2. After simplifying the rules of the game, we can see that this is a typical SG game. However, due to the large range of n, we can't directly calculate the SG value, so we can hit the table to find the law, as follows:

#include<bits/stdc++.h>
#define ll long long
#define maxn 25
int sg[maxn];
int vis[1000] = {0};
using namespace std;

void init() {
	sg[1] = 0;
	for(int i = 2; i<=35; i++) {
		memset(vis, 0, sizeof(vis));
		for(int j = (i + 1) / 2; j < i; j++) vis[sg[j]] = 1;
		for(int j = 0; ; j++) if(!vis[j]) {
				SG[i] = j;
				break;
			}
	}
	for(int i = 1; i<=32; i++) printf("%-2d ",i);
	putchar('\n');
	for(int i = 1; i<=32; i++) printf("%-2d ",sg[i]);
	putchar('\n');
}
int main() {
	init();
	return 0;
}

It can be seen that when n is 2^i - 1, the first hand loses; otherwise, the first hand wins

Nim like games

There are N(N ≤ 20000) heaps of stones, and there are a (1 ≤ a,%) in pile i. two people can take them in turn, one or more at a time (they can take them all), but they can't take them in two or more piles at the same time. The first person can choose one pile at will, but the following rules must be observed each time:

1. If the opponent didn't take away all the stones just now, he can only continue to take them;

2. Only when the opponent takes away all a pile of stones can he take another pile of stones. Whoever takes the last stone will win. Assuming that both sides of the game are extremely clever, who will win?

analysis:

  1. Assuming m 1 and k other arrays, discuss them by case:

  2. Case 1: k = 0, if m is an odd number, the first hand wins

  3. Case 2: k > 0:

    1. m is an odd number: take the pile of non-1 one by one first, and then win first
    2. m is an even number: first take the k - 1 non-1 stack into 1, and then take the last stack, giving the opponent a situation that must be defeated and winning first
  4. If each number is 1 and the total number is even, you will lose first

Topics: Algorithm number theory