PersistentVector 错误,或小型井字棋游戏中的“长”错误

问题描述 投票:0回答:1

我的 clojure tic tac toe 游戏无法运行,并且我丢失了线程......我不确定哪个部分损坏了,并且我无法弄清楚我自己的代码。有人可以帮助我理解为什么我会收到此错误,以及尝试制作井字游戏是否存在任何其他基本问题?

;An implementation of
;Tic-Tac-Toe
;by [Me]

(ns tic-tac-two.main
  (:require [clojure.pprint :refer [pprint]]
            [clojure.string :refer [blank?]]))

(def board-state
  [[" " " " " "]
   [" " " " " "]
   [" " " " " "]])

(defn print-board [board]
  (doseq [row board]
    (println (clojure.string/join " | " (map #(or % " ") row)))))

(defn print-heading []
  (println (str " ======================\n") 
                 "My Tic-Tac-Toe Game\n"
                 "======================\n"))

(defn legal-move? [row col board-state]
  (clojure.string/blank? (get-in board-state [row col])))

(defn check-winner
  "This part of the algorithm reviews the state of the board
   specifically by evaluating the diagonal axis of the board
   and determining whether there are any filled lines in one
   cardinal direction. If so it will return that particular 
   array or nil"
  [board]
  (let [lines (concat board
                      (apply map vector board)
                      [[(get board [0 0]) (get board [1 1]) (get board [2 2])]
                       [(get board [0 2]) (get board [1 1]) (get board [2 0])]])]
    (some (fn [line]
            (when (and (not (every? #(= " " %) line))
                       (every? #(= % (first line)) line))
              (first line)))
          lines)))

(defn make-move
  "Make-move functions by taking appropriate
   symbol ('X' or 'O') into the appropriate
   spot in the state vectors for both the
   player and computer."
  [board row col player]
  (if (blank? (get board [row col]))
    (assoc-in board [row col] player)
    (do
      (println "Invalid move! Spot already taken.")
      board)))
; ex. =>([0 1] [0 2] [1 0] [1 1] [1 2] [2 0] [2 1])

(defn get-available-moves
  "Iterate through the 'open' board positions and
   return a destructured array of available positions
   based on their grid coordinates."
  [board-state]
  (->> (for [row (range (count board-state))
             col (range (count (first board-state)))]
         (when (legal-move? row col board-state)
           [row col]))
       (remove nil?)))

(defn evaluate-board
  "This function applies a score to the potential move
  being evaluated. A score of 0 inevitably continues
  the game and will lead to an updated board state,
  while a 1 or -1 signal the end of the game."
  [board player]
  (cond
    (= (check-winner board) player) 1
    (= (check-winner board) (if (= player "X") "O" "X")) -1
    :else 0))

(defn minimax
  "Minimax is a recursive algorithm that evaluates the
   score and indicating whether the game will progress, 
   or otherwise declaring a winner. Score is updated 
   with each recursive pass."
  [board player depth]
  (if (empty? (get-available-moves board))
    (evaluate-board board player)
    (let [available-moves (get-available-moves board)
          scores-and-moves (for [[row col] available-moves]
                             (let [new-board (make-move board row col player)
                                   score (minimax new-board (if (= player "X") "O" "X") (dec depth))]
                               [score [row col]]))]
      (if (= player "X")
        (apply max-key first scores-and-moves)
        (apply min-key first scores-and-moves)))))

(defn valid-input? [input]
  (and (not (blank? input)) (some #(= (Integer/parseInt input) %) [0 1 2])))

(defn get-valid-input
  "This function ensures that the user can only enter an integer [0 1 2]"
  [prompt valid-fn]
  (loop []
    (println prompt)
    (let [input (read-line)]
      (if (valid-fn input)
        (Integer/parseInt input)
        (do
          (println "Invalid input. Please try again.")
          (recur))))))

(defn -main
  "This is the main game loop. It first prints a heading
   and then takes user input. It will print an updated board
   each turn and continue until one of the players have one;
   correctly identifying the winner, declaring them the winner,
   and then gracefully exiting."
  []
  (let [_ (do
            (print-heading)
            (println "  Enter your REAL name"))
        player-name (read-line)
        _ (println (str "Okay " player-name ", welcome to the game!"))]
    (loop [current-board board-state] 
      (let [_ (print-board current-board)
            winner (check-winner current-board)
            winner-name (if (= winner "X") player-name
                            "Computer...")]
        (if winner
          (println (str "Player " winner-name " wins!"))
          (let [row (get-valid-input "Enter your row [0, 1, 2]:" #(valid-input? %))
                col (get-valid-input "Enter your column [0, 1, 2]:" #(valid-input? %))
                board-after-player-move (make-move current-board row col "X")
                [ai-score [ai-row ai-col]] (minimax board-after-player-move "O" 9)
                board-after-computer-move (make-move board-after-player-move ai-row ai-col "O")]
            (recur board-after-computer-move)))))))

您能否帮助我理解为什么会出现这种持续性矢量错误,并让我知道我的方法和代码风格是否存在根本问题?

; tic-tac-two.main/minimax (REPL:98) 处出现执行错误 (ClassCastException)。 ; clojure.lang.PersistentVector 类无法转换为 java.lang.Number 类(clojure.lang.PersistentVector 位于加载程序“app”的未命名模块中;java.lang.Number 位于加载程序“bootstrap”的 java.base 模块中)

在 repl 返回中使用 *e

*e
#error {:cause "class clojure.lang.PersistentVector cannot be cast to class java.lang.Number (clojure.lang.PersistentVector is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')"
        :via
        [{:type java.lang.ClassCastException
          :message "class clojure.lang.PersistentVector cannot be cast to class java.lang.Number (clojure.lang.PersistentVector is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')"
          :at [clojure.lang.Numbers lt "Numbers.java" 253]}]
        :trace
        [[clojure.lang.Numbers lt "Numbers.java" 253]
         [clojure.core$min_key invokeStatic "core.clj" 5069]
         [clojure.core$min_key invoke "core.clj" 5062]
         [clojure.lang.AFn applyToHelper "AFn.java" 160]
         [clojure.lang.RestFn applyTo "RestFn.java" 135]
         [clojure.core$apply invokeStatic "core.clj" 669]
         [clojure.core$apply invoke "core.clj" 662]
         [tic_tac_two.main$minimax invokeStatic "NO_SOURCE_FILE" 96]
         [tic_tac_two.main$minimax invoke "NO_SOURCE_FILE" 80]
         [tic_tac_two.main$minimax$iter__11179__11183$fn__11184$fn__11185 invoke "NO_SOURCE_FILE" 92]
         [tic_tac_two.main$minimax$iter__11179__11183$fn__11184 invoke "NO_SOURCE_FILE" 90]
         [clojure.lang.LazySeq force "LazySeq.java" 50]
         [clojure.lang.LazySeq realize "LazySeq.java" 89]
         [clojure.lang.LazySeq seq "LazySeq.java" 106]
         [clojure.lang.Cons next "Cons.java" 41]
         [clojure.lang.RT boundedLength "RT.java" 1810]
         [clojure.lang.RestFn applyTo "RestFn.java" 133]
         [clojure.core$apply invokeStatic "core.clj" 669]
         [clojure.core$apply invoke "core.clj" 662]
         [tic_tac_two.main$minimax invokeStatic "NO_SOURCE_FILE" 95]
         [tic_tac_two.main$minimax invoke "NO_SOURCE_FILE" 80]
         [tic_tac_two.main$minimax$iter__11179__11183$fn__11184$fn__11185 invoke "NO_SOURCE_FILE" 92]
         [tic_tac_two.main$minimax$iter__11179__11183$fn__11184 invoke "NO_SOURCE_FILE" 90]
         [clojure.lang.LazySeq force "LazySeq.java" 50]
         [clojure.lang.LazySeq realize "LazySeq.java" 89]
         [clojure.lang.LazySeq seq "LazySeq.java" 106]
         [clojure.lang.Cons next "Cons.java" 41]
         [clojure.lang.RT boundedLength "RT.java" 1810]
         [clojure.lang.RestFn applyTo "RestFn.java" 133]
         [clojure.core$apply invokeStatic "core.clj" 669]
         [clojure.core$apply invoke "core.clj" 662]
         [tic_tac_two.main$minimax invokeStatic "NO_SOURCE_FILE" 96]
         [tic_tac_two.main$minimax invoke "NO_SOURCE_FILE" 80]
         [tic_tac_two.main$minimax$iter__11179__11183$fn__11184$fn__11185 invoke "NO_SOURCE_FILE" 92]
         [tic_tac_two.main$minimax$iter__11179__11183$fn__11184 invoke "NO_SOURCE_FILE" 90]
         [clojure.lang.LazySeq force "LazySeq.java" 50]
         [clojure.lang.LazySeq realize "LazySeq.java" 89]
         [clojure.lang.LazySeq seq "LazySeq.java" 106]
         [clojure.lang.Cons next "Cons.java" 41]
         [clojure.lang.RT boundedLength "RT.java" 1810]
         [clojure.lang.RestFn applyTo "RestFn.java" 133]
         [clojure.core$apply invokeStatic "core.clj" 669]
         [clojure.core$apply invoke "core.clj" 662]
         [tic_tac_two.main$minimax invokeStatic "NO_SOURCE_FILE" 95]
         [tic_tac_two.main$minimax invoke "NO_SOURCE_FILE" 80]
         [tic_tac_two.main$minimax$iter__11179__11183$fn__11184$fn__11185 invoke "NO_SOURCE_FILE" 92]
         [tic_tac_two.main$minimax$iter__11179__11183$fn__11184 invoke "NO_SOURCE_FILE" 90]
         [clojure.lang.LazySeq force "LazySeq.java" 50]
         [clojure.lang.LazySeq realize "LazySeq.java" 89]
         [clojure.lang.LazySeq seq "LazySeq.java" 106]
         [clojure.lang.Cons next "Cons.java" 41]
         [clojure.lang.RT boundedLength "RT.java" 1810]
         [clojure.lang.RestFn applyTo "RestFn.java" 133]
         [clojure.core$apply invokeStatic "core.clj" 669]
         [clojure.core$apply invoke "core.clj" 662]
         [tic_tac_two.main$minimax invokeStatic "NO_SOURCE_FILE" 96]
         [tic_tac_two.main$minimax invoke "NO_SOURCE_FILE" 80]
         [tic_tac_two.main$minimax$iter__11179__11183$fn__11184$fn__11185 invoke "NO_SOURCE_FILE" 92]
         [tic_tac_two.main$minimax$iter__11179__11183$fn__11184 invoke "NO_SOURCE_FILE" 90]
         [clojure.lang.LazySeq force "LazySeq.java" 50]
         [clojure.lang.LazySeq realize "LazySeq.java" 89]
         [clojure.lang.LazySeq seq "LazySeq.java" 106]
         [clojure.lang.Cons next "Cons.java" 41]
         [clojure.lang.RT boundedLength "RT.java" 1810]
         [clojure.lang.RestFn applyTo "RestFn.java" 133]
         [clojure.core$apply invokeStatic "core.clj" 669]
         [clojure.core$apply invoke "core.clj" 662]
         [tic_tac_two.main$minimax invokeStatic "NO_SOURCE_FILE" 95]
         [tic_tac_two.main$minimax invoke "NO_SOURCE_FILE" 80]
         [tic_tac_two.main$minimax$iter__11179__11183$fn__11184$fn__11185 invoke "NO_SOURCE_FILE" 92]
         [tic_tac_two.main$minimax$iter__11179__11183$fn__11184 invoke "NO_SOURCE_FILE" 90]
         [clojure.lang.LazySeq force "LazySeq.java" 50]
         [clojure.lang.LazySeq realize "LazySeq.java" 89]
         [clojure.lang.LazySeq seq "LazySeq.java" 106]
         [clojure.lang.Cons next "Cons.java" 41]
         [clojure.lang.RT boundedLength "RT.java" 1810]
         [clojure.lang.RestFn applyTo "RestFn.java" 133]
         [clojure.core$apply invokeStatic "core.clj" 669]
         [clojure.core$apply invoke "core.clj" 662]
         [tic_tac_two.main$minimax invokeStatic "NO_SOURCE_FILE" 96]
         [tic_tac_two.main$minimax invoke "NO_SOURCE_FILE" 80]
         [tic_tac_two.main$_main invokeStatic "NO_SOURCE_FILE" 135]
         [tic_tac_two.main$_main invoke "NO_SOURCE_FILE" 113]
         [tic_tac_two.main$eval11215 invokeStatic "NO_SOURCE_FILE" 1]
         [tic_tac_two.main$eval11215 invoke "NO_SOURCE_FILE" 1]
         [clojure.lang.Compiler eval "Compiler.java" 7700]
         [clojure.lang.Compiler eval "Compiler.java" 7655]
         [clojure.core$eval invokeStatic "core.clj" 3232]
         [clojure.core$eval invoke "core.clj" 3228]
         [nrepl.middleware.interruptible_eval$evaluate$fn__1357$fn__1358 invoke "interruptible_eval.clj" 87]
         [clojure.lang.AFn applyToHelper "AFn.java" 152]
         [clojure.lang.AFn applyTo "AFn.java" 144]
         [clojure.core$apply invokeStatic "core.clj" 667]
         [clojure.core$with_bindings_STAR_ invokeStatic "core.clj" 1990]
         [clojure.core$with_bindings_STAR_ doInvoke "core.clj" 1990]
         [clojure.lang.RestFn invoke "RestFn.java" 428]
         [nrepl.middleware.interruptible_eval$evaluate$fn__1357 invoke "interruptible_eval.clj" 87]
         [clojure.main$repl$read_eval_print__9244$fn__9247 invoke "main.clj" 437]
         [clojure.main$repl$read_eval_print__9244 invoke "main.clj" 437]
         [clojure.main$repl$fn__9253 invoke "main.clj" 459]
         [clojure.main$repl invokeStatic "main.clj" 459]
         [clojure.main$repl doInvoke "main.clj" 368]
         [clojure.lang.RestFn invoke "RestFn.java" 1526]
         [nrepl.middleware.interruptible_eval$evaluate invokeStatic "interruptible_eval.clj" 84]
         [nrepl.middleware.interruptible_eval$evaluate invoke "interruptible_eval.clj" 56]
         [nrepl.middleware.interruptible_eval$interruptible_eval$fn__1390$fn__1394 invoke "interruptible_eval.clj" 152]
         [clojure.lang.AFn run "AFn.java" 22]
         [nrepl.middleware.session$session_exec$main_loop__1460$fn__1464 invoke "session.clj" 218]
         [nrepl.middleware.session$session_exec$main_loop__1460 invoke "session.clj" 217]
         [clojure.lang.AFn run "AFn.java" 22]
         [java.lang.Thread run "Thread.java" 1583]]}```
algorithm clojure console-application tic-tac-toe minimax
1个回答
0
投票

堆栈跟踪中提到您的代码的最上面的条目是关于

tic_tac_two.main$minimax
。那里的
$
符号是由于 Clojure 代码如何转换为 JVM 字节码而导致的 - 您可以在心里用
/
替换它。与
_
相同 - 将其替换为
-
。这给了我们
tic-tac-two.main/minimax
,它直接指向有问题的函数。

向上移动堆栈跟踪,提及您的

minimax
函数实际使用的函数的最近条目是
clojure.core$min_key
- 对应于
clojure.core/min-key

来自该函数的文档字符串:

返回 (k x)(一个数字)最小的 x。

因此它期望 key 函数为集合中的每个实体返回一个数字。但

scores-and-moves
集合是使用
for
创建的惰性序列,仅包含向量。因此,错误基本上表明您不能将向量视为数字。

假设您想根据分数找到最低(以及最高,在

max-key
的情况下)条目,请尝试将对
first
的调用替换为
ffirst

© www.soinside.com 2019 - 2024. All rights reserved.