;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Spider state evaluation

;;; Evaluation function features

(defun spider-hidden-cards (state &aux (count 0))
  (map nil #'(lambda (stack) (incf count (count-if #'card-hidden? stack)))
       (spider-state-stacks state))
  count)

(defun spider-open-spaces  (state &aux (count 0))
    (map nil #'(lambda (stack) (when (null stack) (incf count)))
       (spider-state-stacks state))
  count)

(defun spider-suit-changes (state &aux (count 0))
    (map nil #'(lambda (stack) (incf count (stack-suit-changes stack 0)))
       (spider-state-stacks state))
  count)

(defun stack-suit-changes (cards n &optional (previous-suit nil) (previous-number nil))
  "Return the number of within-sequence suit changes in cards."
  (cond ((or (null cards) (card-hidden? (first cards)))
	 n)
	((or (not (eql (1- (card-number (first cards))) previous-number))
	     (eql (card-suit (first cards)) previous-suit))
	 (stack-suit-changes (rest cards) n (card-suit (first cards)) (card-number (first cards))))
	(t (stack-suit-changes (rest cards) (1+ n) (card-suit (first cards)) (card-number (first cards))))))

(defun spider-sequence-changes (state &aux (count 0))
    (map nil #'(lambda (stack) (incf count (stack-sequence-changes stack 0)))
       (spider-state-stacks state))
  count)

(defun stack-sequence-changes (cards n &optional (previous-number nil))
  "Return the number of sequence changes in cards."
  (cond ((or (null cards) (card-hidden? (first cards)))
	 n)
	((or (null previous-number) (eql (1- (card-number (first cards))) previous-number))
	 (stack-sequence-changes (rest cards) n (card-number (first cards))))
	(t (stack-sequence-changes (rest cards) (1+ n) (card-number (first cards))))))

(defun spider-pure-stacks (state &aux (count 0))
    (map nil #'(lambda (stack) (when (pure-stack? stack) (incf count)))
       (spider-state-stacks state))
  count)

(defun pure-stack? (cards &optional (previous-suit nil) (previous-number nil))
  "Return t iff stack is a single same-suit sequence (plus hidden cards)."
  (cond ((or (null cards) (card-hidden? (first cards))) t)
	((or (null previous-number) 
	     (and (eql (1- (card-number (first cards))) previous-number)
		  (eql (card-suit (first cards)) previous-suit)))
	 (pure-stack? (rest cards) (card-suit (first cards)) (card-number (first cards))))
	(t nil)))


(defun spider-suits-completed (state)
  (length (spider-state-completed state)))

(defun spider-top-diversity (state &aux (numbers nil))
    (map nil #'(lambda (stack) (when (and stack (not (card-hidden? (first stack))))
				 (pushnew (card-number (first stack)) numbers)))
       (spider-state-stacks state))
  (length numbers))

;;; Linear evaluation function

(defvar *spider-features*)
(setq *spider-features*
  (list #'spider-hidden-cards 
	#'spider-open-spaces  
	#'spider-suit-changes 
	#'spider-sequence-changes 
	#'spider-pure-stacks 
	#'spider-suits-completed 
	#'spider-top-diversity
	))

(defvar *spider-weights*)
(setq *spider-weights*
  '(-0.5    ; spider-hidden-cards 
    +10.0    ; spider-open-spaces  
    -1.0    ; spider-suit-changes 
    -3.0    ; spider-sequence-changes 
    +2.0    ; spider-pure-stacks 
    +30.0    ; spider-suits-completed 
    +0.5    ; spider-top-diversity
    ))

(defun spider-eval (state)
  "Return a value for state using a linear combination of features."
  (linear-eval state *spider-features* *spider-weights*))