// file: nim.C // author: Robert Keller // purpose: nim-playing program originally developed in rex, transcribed to C++ // compile: g++ nim.C -L. -lpolya -lm // User types a list of numbers representing nim piles. Program responds with // a revised set of piles. The version of nim being played is that the // player taking the last stone wins. // This code is inadequately documented as it stands. // Please see info/nim.doc and the solutions to a2 for more information. #include "polya.H" // prototypes Polylist arbitrary_move(Polylist L); int bad_input(Polylist L); Polylist decrement_max(Polylist L, integer M); Polylist make_nim_sum_zero(integer Sum, Polylist L); integer max_list(Polylist L); Polylist nim(Polylist L); integer nim_sum(integer M, integer N); integer nim_sum_list(Polylist L); main() { Poly P; while( cin >> P ) { if( bad_input(P) ) cout << "sorry, only lists of positive integers are allowed" << endl; else { Polylist L = P; cout << nim(L) << endl; } } } // // return a new list of piles // Polylist nim(Polylist L) { integer Sum = nim_sum_list(L); if( Sum == 0 ) return arbitrary_move(L); else return make_nim_sum_zero(Sum, L); } // // the main strategy // Polylist make_nim_sum_zero(integer Sum, Polylist L) { integer pile = first(L); Polylist piles = rest(L); integer delta = nim_sum(Sum, pile); if( delta <= pile ) { if( delta == 0 ) return piles; else return cons(delta, piles); } else return cons(pile, make_nim_sum_zero(Sum, piles)); } // // compute the nim sum // integer nim_sum(integer M, integer N) { if( M == 0 ) return N; if( N == 0 ) return M; return (M+N)%2 + 2*nim_sum(M/2, N/2); } // // compute the nim sum of a list // integer nim_sum_list(Polylist L) { if( isEmpty(L) ) return 0; else return nim_sum(first(L), nim_sum_list(rest(L))); } // // make a move when you can't guarantee a win // Polylist arbitrary_move(Polylist L) { return decrement_max(L, max_list(L)); } // // decrement the largest pile // Polylist decrement_max(Polylist L, integer M) { integer F = first(L); if( F == M ) return F > 1 ? cons(M-1, rest(L)) : rest(L); return cons(F, decrement_max(rest(L), M)); } // // find the max of a non-empty list // integer max_list(Polylist L) { integer result = first(L); L = rest(L); while( !isEmpty(L) ) { if( integer(first(L)) > result ) result = integer(first(L)); L = rest(L); } return result; } // // test for bad input // int bad_input(Polylist L) { if( !isList(L) || isEmpty(L) ) return 1; while( !isEmpty(L) ) { if( !isInteger(first(L)) || integer(first(L)) <= 0 ) return 1; L = rest(L); } return 0; }