Clojure: перебирание вектора векторов, чтобы найти первый вектор, который удовлетворяет определенному условию


Чтобы выучить Clojure, я работаю над маленькой игрой в крестики-нолики. После завершения первой части игры с относительной легкостью, я изо всех сил пытался построить интеллектуальный компьютерный плеер.

Для теста, который я пишу, чтобы помочь в этом, я хочу проверить, что компьютер выбирает место 9, если это очередь компьютера, и это доска:

X / O / 3
4 / X / O
7 | 8 | 9

Чтобы начать игру, доска определяется следующим образом, как карта с парами ключ-значение представление местоположения на доске и содержимого этого пространства:

(def board {1 "1" 2 "2" 3 "3" 4 "4" 5 "5" 6 "6" 7 "7" 8 "8" 9 "9"})
У меня было несколько мыслей о том, как решить эту проблему. Один из них состоял в том, чтобы определить выигрышные наборы следующим образом:
(def winning-sets
    [[(board 1) (board 2) (board 3)],
     [(board 4) (board 5) (board 6)],
     [(board 7) (board 8) (board 9)],
     [(board 1) (board 4) (board 7)],
     [(board 2) (board 5) (board 8)],
     [(board 3) (board 6) (board 9)],
     [(board 1) (board 5) (board 9)],
     [(board 3) (board 5) (board 7)]])

Перебираем каждое множество:

(for [set winning-sets]
    (filter #(= symbol %) set))
Но это кажется неправильным...Я не знаю, куда бы я пошел оттуда. Проблему, которую я пытаюсь решить, можно описать следующим образом:

Попросите компьютер просмотреть 8 выигрышных наборов и найти один набор, в котором есть два ваших символа и одно открытое место.

Я довольно новичок в Clojure, поэтому я просто не уверен, что понимаю, как лучше всего подойти к этой проблеме. Я смотрел на ClojureDocs (проверяя итерационные функции, такие как for и loop и case), но не смог сделать эту работу.

Каким будет лучший способ перебрать эти выигрышные наборы, в настоящее время в векторной форме, и найти набор, который имеет два определенных символа и один открывающий? Или было бы лучше, чтобы ... хранить выигрышные наборы в другой структуре данных?

примечание: Я прочитал ответы на этот вопрос , но не смог применить их к своему.

1 5

1 ответ:

Прежде всего, я советую вам использовать эту структуру для позиции правления:

(def board [[1 1 0] 
            [0 0 0] 
            [1 0 1]])
Где X - 1, O - -1, а пустая ячейка - 0. Доска в моем примере имеет только символы X (для упрощения). Далее,
(def winning-sets
  '([[0 0] [0 1] [0 2]] 
    [[1 0] [1 1] [1 2]] 
    [[2 0] [2 1] [2 2]]
    [[0 0] [1 0] [2 0]] 
    [[0 1] [1 1] [2 1]] 
    [[0 2] [1 2] [2 2]]
    [[0 0] [1 1] [2 2]]
    [[0 2] [1 1] [2 0]]))
Это "выигрышные" наборы координат. Вы можете вычислить это при необходимости, но для 3x3 этот список действительно не так велик. В этом смысле ответ на ваш вопрос будет
(defn check 
  [target combo] 
  (= (map #(count (filter (partial = %) combo)) [target 0]) '(2 1)))

(defn extract 
  [coords] 
  (apply vector (map (fn [[f s]] ((board f) s)) coords))) 

(filter #(check 1 (extract %)) winning-sets)

Если вы выполните этот код в REPL, вы увидите

user=> (filter #(check 1 (extract %)) winning-sets)
([[0 0] [0 1] [0 2]] 
 [[2 0] [2 1] [2 2]] 
 [[0 0] [1 0] [2 0]] 
 [[0 0] [1 1] [2 2]])

Который выглядит так: правильный ответ (2 горизонтальные линии, 1 вертикальная и 1 диагональная). Код груб, и есть несколько способов сделать его более красивым и многоразовым. Должен ли я объяснить, что происходит, или код достаточно ясен?