open XPrelude

exception Illegal_move of (Board.piece * Board.move);;

type status =
    Winner of Board.piece * int * int
  | Resign of Board.piece * int * int
  | Tie of int * int;;
      
type playerFun = Board.t -> Board.move option;;
  
exception Exit_game of status;;

let printBoard brd =
  Board.printBoard brd;
  List.iter (function m -> Printf.printf "%s, " (Board.string_of_move m))
    (Board.legalMoves brd (Board.turn brd))
  
(* type playerFun = (Board.t -> Board.move option) *)
let play blackPlayer whitePlayer init print =
  let board = ref init
  and lastMove = ref Board.Pass in
  try
    while ((!lastMove <> Board.Pass) ||
           (Board.canMove !board (Board.turn !board))) do
      if print then Board.printBoard !board; flush stdout;
      let player = Board.turn !board in
      let playerf = 
	if player = Board.Black then blackPlayer else whitePlayer in
      match (playerf !board) with
      	None ->
	  let (b, w) = Features.pieceCount !board in
	  raise (Exit_game (Resign(player, b, w)))
      | Some move -> begin
	  lastMove := move;
    	  if not (Board.legalMove !board player move) then
	    raise (Illegal_move (player, move))
    	  else
	    board := Board.makeMove !board player move
      end;
    done;
    if (Board.turn !board) = Board.Black then
      (ignore (blackPlayer !board); ignore (whitePlayer !board))
    else
      (ignore (whitePlayer !board); ignore (blackPlayer !board));
    if print then Board.printBoard !board; flush stdout;
    let (b, w) = Features.pieceCount !board in
    if b > w then
      Winner(Board.Black, b, w)
    else if b = w then
      Tie(b, w)
    else
      Winner(Board.White, b, w)
  with Exit_game s -> s;;
    
    
let randomPlayer brd =
  let moves = Board.legalMoves brd (Board.turn brd) in
  Some (List.nth moves (Random.int (List.length moves)));;


let humanPlayer =
  let move_of_string s =
    match (Stream.next (Genlex.make_lexer [] (Stream.of_string s))) with
      Genlex.Ident tok -> begin
	match (String.uppercase tok) with
	  "PASS" -> Some Board.Pass
	| "RESIGN" -> None
	| m ->
	    if ((String.length tok) = 2 &
		List.mem m.[0] ['A';'B';'C';'D';'E';'F';'G';'H'] &
		List.mem tok.[1] ['1';'2';'3';'4';'5';'6';'7';'8']) then
	      let row = Char.code m.[1] - Char.code '1'
	      and col = Char.code m.[0] - Char.code 'A' in
	      Some (Board.Square (row * 8 + col))
	    else raise Stream.Failure
      end
    | _ -> raise Stream.Failure
  in function brd ->
    let moves = Board.legalMoves brd (Board.turn brd)
    and gotit = ref false
    and move = ref None in
    while not !gotit do
      print_string "Avaliable moves: ";
      List.iter
	(function m -> print_string ((Board.string_of_move m)^", ")) moves;
      print_string "\nEnter a move: ";
      let moveStr = read_line() in
      try
	move := move_of_string moveStr;
	match !move with
	  None -> gotit := true
	| Some m ->
	    if List.mem m moves then
	      gotit := true
	    else
	      print_string "Illegal move entered\n"
      with Stream.Failure -> print_string "Invalid entry\n"
    done;
    !move;;
