All pastes #1806303 Raw Edit

Nim Minimax

public text v1 · immutable
#1806303 ·published 2010-02-23 02:25 UTC
rendered paste body
//Minimax principle implemented for the Nim game (1 row)
//by Marc Burns (C) 2009
#include <iostream>
#include <cstdlib>
#include <string>
#include <cmath>

using namespace std;

int user_simulate(int remain, int take, int depth=1);

//0 is returned on computer loss or invalid move
//depth is returned on user loss in a valid move.
//Goal is to find the shortest path to a computer win
//given the initial remain and take parameters.
int comp_simulate(int remain, int take, int depth=1)
{
	if(remain == 1) //computer lost at this point
	{
		return 0; 
	}

	if(take >= remain) //can not play
		return 0;
	
	//return min. path depth
	return min(user_simulate(remain-take, 1, depth+1), min(user_simulate(remain-take, 2, depth+1), user_simulate(remain-take, 3, depth+1)));
}

int user_simulate(int remain, int take, int depth)
{
	if(remain == 1) //user lost at this point
		return depth; //the lowest nonzero value returned is selected by mutual recursion

	if(take >= remain) //can not play
		return 0;

	//effectively return nonzero depth
	return max(comp_simulate(remain-take, 1, depth+1), max(comp_simulate(remain-take, 2, depth+1), comp_simulate(remain-take, 3, depth+1)));
}

void compvcomp(int num_matches)
{
	int turn = 1;
	while(num_matches > 1)
	{
		cout << num_matches << " matches remain: ";
		turn = 1 - turn;
		int m = 1;
		int v1 = comp_simulate(num_matches, 1); //returns depth of closest win
		int v2 = comp_simulate(num_matches, 2); //for each possible choice.
		int v3 = comp_simulate(num_matches, 3);

		//choose path with least chance of loss
		int abmax = max(v1,max(v2,v3))+1;
		if(v1 == 0) v1 = abmax; //convert 0 paths (no chances win) into
		if(v2 == 0) v2 = abmax; //paths of greatest depth (least preference).
		if(v3 == 0) v3 = abmax;

		if(v3 <= v1 && v3 <= v2) //find path of least depth
			m = 3;
		if(v2 <= v1 && v2 <= v3)
			m = 2;
		if(v1 <= v2 && v1 <= v3)
			m = 1;
			
		if(m >= num_matches) //this should never happen
		{
			cout << "Minimax error: want = " << m << "\n";
			continue;
		}
		num_matches -= m;
		cout << "Computer " << ((turn==0)?'A':'B') << " takes " << m << " matches.\n";
	}
	if(turn == 0)
		cout << "Computer A wins.\n";
	else
		cout << "Computer B wins.\n";
}

int main()
{
	string buf; //for various input ops
	int num_matches = 0; //number of matches to play

	while(num_matches <= 0)
	{
		cout << "How many matches should be used to play? ";
		getline(cin, buf);
		num_matches = atoi(buf.c_str());
	}

	cout << "Would you like to play [I]nteractively or [O]bserve? ";
	getline(cin, buf);
	if((buf.c_str()[0] | 32) == 'o')
	{
		compvcomp(num_matches);
		return 0;
	}

	int turn = 1; //0=user, 1=computer
	cout << "Should the [U]ser or [C]omputer move first? ";
	getline(cin, buf);
	if((buf.c_str()[0] | 32) == 'c')
		turn = 0; //turn is negated in game loop, so use opposite value.

	while(num_matches > 1)
	{
		turn = 1 - turn; //negate turn
		if(turn == 0)
			cout << "User's turn: ";
		else
			cout << "Computer's turn: ";

		cout << num_matches << " matches remain.\n";

		if(turn == 0)
		{
			int m = 0;
			while(m <= 0 || m > 3)
			{
				cout << "How many matches will you remove? (1-3) ";
				getline(cin, buf);
				m = atoi(buf.c_str());
				if(m >= num_matches)
					m = 0;
			}
			num_matches -= m;
		} else {
			int m = 1;
			int v1 = comp_simulate(num_matches, 1); //returns depth of closest win
			int v2 = comp_simulate(num_matches, 2); //for each possible choice.
			int v3 = comp_simulate(num_matches, 3);

			//choose path with least chance of loss
			int abmax = max(v1,max(v2,v3))+1;
			if(v1 == 0) v1 = abmax; //convert 0 paths (no chances win) into
			if(v2 == 0) v2 = abmax; //paths of greatest depth (least preference).
			if(v3 == 0) v3 = abmax;

			if(v3 <= v1 && v3 <= v2) //find path of least depth
				m = 3;
			if(v2 <= v1 && v2 <= v3)
				m = 2;
			if(v1 <= v2 && v1 <= v3)
				m = 1;
			

			if(m >= num_matches) //this should never happen
			{
				cout << "Minimax error: want = " << m << "\n";
				continue;
			}
			num_matches -= m;
			cout << "Computer takes " << m << " matches.\n";
		}
		cout << "\n";
	}

	if(turn == 0)
		cout << "User wins.\n";
	else
		cout << "Computer wins.\n";

	return 0;	
}