(define empty-board
  (lambda () (make-vector 9 '?)))

(define print-key
  (lambda ()
    (newline)
    (display " 0 | 1 | 2 ") 
    (newline)
    (display "---+---+---")
    (newline)
    (display " 3 | 4 | 5 ")
    (newline)
    (display "---+---+---")
    (newline)
    (display " 6 | 7 | 8 ")
    (newline)
    (newline)))

(define print-board
  (lambda (b)
    (let ((show
	   (lambda (i b)
	     (let ((x (vector-ref b i)))
	       (cond ((eq? x 'x) (display " X "))
		     ((eq? x 'o) (display " O "))
		     (else (display "   ")))))))
      (newline)
      (show 0 b)
      (display "|")
      (show 1 b)
      (display "|")
      (show 2 b)
      (newline)
      (display "---+---+---")
      (newline)
      (show 3 b)
      (display "|")
      (show 4 b)
      (display "|")
      (show 5 b)
      (newline)
      (display "---+---+---")
      (newline)
      (show 6 b)
      (display "|")
      (show 7 b)
      (display "|")
      (show 8 b)
      (newline))))

(define empties
  (lambda (b)
    (letrec 
      ((loop 
	(lambda (b i)
	  (cond ((= i 9) '())
		((eq? (vector-ref b i) '?)
		 (cons i (loop b (add1 i))))
		(else
		 (loop b (add1 i)))))))
      (loop b 0))))

(define best-move-o
  (lambda (b)
    (let* ((moves (empties b))
	   (vals (map (lambda (i) (max! b i 1)) moves))
	   (optimum (apply min vals)))
      (cadr (assoc optimum 
		   (map list vals moves))))))

(define play-o
  (lambda ()
    (letrec 
      ((b (empty-board))
       (loop 
         (lambda (b)
	   (time (vector-set! b (best-move-o b) 'o))
	   (newline)
	   (print-board b)
	   (if (= (evaluate-board b) -1)
	       (begin
		 (display "I win!")
		 (newline)
		 )
	       (begin
		 (display "Input move: ")
		 (vector-set! b (read) 'x)
		 (print-board b)
		 (if (= (evaluate-board b) 1)
		     (begin
		       (display "You win!")
		       (newline)
		       )
		       (if (null? (empties b))
			   (begin
			     (display "It's a draw.")
			     (newline)
			     )
			   (loop b))
		       ))))))
      (display "Input move: ")
      (vector-set! b (read) 'x)
      (print-board b)
      (loop b))))

(define open-moves?
  (lambda (b)
    (not (null? (empties b)))))

(define max!
  (lambda (b i alpha)
    (vector-set! b i 'o)
    (letrec 
      ((do-moves
	(lambda (i beta)
	  (cond ((or (>= beta alpha) (= beta 1) (= i 9)) beta)
		((eq? (vector-ref b i) '?)
		 (do-moves (add1 i) (max beta (min! b i beta))))
		(else
		 (do-moves (add1 i) beta)))))
       (result 
	(cond ((= (evaluate-board b) -1) -1)
	      ((open-moves? b) 
	       (do-moves 0 -1))
	      (else 0))))
      (vector-set! b i '?)
      result)))

(define min!
  (lambda (b i beta)
    (vector-set! b i 'x)
    (letrec 
      ((do-moves
	(lambda (i alpha)
	  (cond ((or (<= alpha beta) (= alpha -1) (= i 9)) alpha)
		((eq? (vector-ref b i) '?)
		 (do-moves (add1 i) (min alpha (max! b i alpha))))
		(else
		 (do-moves (add1 i) alpha)))))
       (result 
	(cond ((= (evaluate-board b) 1) 1)
	      ((open-moves? b) 
	       (do-moves 0 1))
	      (else 0))))
      (vector-set! b i '?)
      result)))