수정일: 2023년 08월 15일
4clojure - Analyze a Tic-Tac-Toe Board (73)
문제
(= nil (__ [[:e :e :e]
[:e :e :e]
[:e :e :e]]))
(= :x (__ [[:x :e :o]
[:x :e :e]
[:x :e :o]]))
(= :o (__ [[:e :x :e]
[:o :o :o]
[:x :e :x]]))
(= nil (__ [[:x :e :o]
[:x :x :e]
[:o :x :o]]))
(= :x (__ [[:x :e :e]
[:o :x :e]
[:o :e :x]]))
(= :o (__ [[:x :e :o]
[:x :o :e]
[:o :e :x]]))
(= nil (__ [[:x :o :x]
[:x :o :x]
[:o :x :o]]))
풀이
(fn [coll]
(let [a (flatten (partition 1 4 (flatten coll)))
r (flatten (partition 1 4 (flatten (map #(reverse %) coll))))
b (into (into (into coll (apply map vector coll)) [a]) [r])]
(cond (some true? (map #(every? (fn [x] (= x :x)) %) b)) :x
(some true? (map #(every? (fn [x] (= x :o)) %) b)) :o
:else nil)))
풀이 과정
sort 해서 하면 되겠거니 했었는데 일직선이 되지 않는 값들에 대해서가 문제가 된다. 그래서 좀 더 생각해야 할 것 같다.
틱택토 게임은 기본적으로 빙고와 같다고 생각 한다.
일직선에 3개는 일단 해결이 될 것 같은데 밑으로는 어떻게 검증을 해야 하나 생각을 했을 때 밑으로 collection을 만들어 주면 될 것 같다는 생각이 들었다.
(apply map vector [[:x :e :o] [:x :e :e] [:x :e :o]])
;; ([:x :x :x] [:e :e :e] [:o :e :o])
대각선도 위와 같은 형태로 만들어서 검증을 할 수 있는 로직을 짜면 해결이 될 것 같다.
group-by로 검증 로직을 할 수 있을 것 같은데 좀 더 고민이 필요하다.
이 문제를 풀기 위한 데이터 구조가 어떻게 되는지 생각을 해봐야겠다.
내 생각에는 수직, 수평, 대각선 각각의 값들을 가진 3개의 백터 쌍으로 있는 collection을 먼저 만들어 놓고 검증을 하는 것이 낫겠다고 생각한다.
수평, 수직은 어떻게든 될 것 같은데 대각선을 어떻게 추출을 해야할지 좀 고민이 된다. 전통적인 방식으로만 하려고 생각하다보니 그것에는 적합하지 않다는 것을 느끼고 있다. 그래서 더 힘든 것 같다. 생각의 전환이라는 것이 한번 생각이 들면 바꿔서 생각을 한다는 것이 쉬운 일은 아니다.
검증은 every?로 하면 한 collection내의 값이 모두 동일해야 참이라는 것을 알 수 있다.
(every? #(= % :x) [:x :x :x])
;; true
(every? #(= % :x) [:x :x :o])
;; false
우선 수평, 수직에 대한 자료형을 만들어 봤다
(into [[:x :e :o] [:x :e :e] [:x :e :o]] (apply map vector [[:x :e :o] [:x :e :e] [:x :e :o]]))
;; [[:x :e :o] [:x :e :e] [:x :e :o] [:x :x :x] [:e :e :e] [:o :e :o]]
그리고 검사식은 아래와 같다. :x에 대한 every?가 true이고 그 결과를 가지고 true가 1개라도 있으면 true인 결과를 반환한다.
(some true? (map #(every? (fn [x] (= x :x)) %) [[:x :e :o] [:x :e :e] [:x :e :o] [:x :x :x] [:e :e :e] [:o :e :o]]))
;; true
대각선을 제외하고 4개 통과하는 프로그램이다.
(fn [coll]
(let [b (into coll (apply map vector coll))]
(cond (some true? (map #(every? (fn [x] (= x :x)) %) b)) :x
(some true? (map #(every? (fn [x] (= x :o)) %) b)) :o
:else nil)))
대각선을 가지고 오는 방법은 first, second, last인데.. 일단 여기까지만 생각이 난다. 반대 대각선은 last, second, first… 이 조합으로 구성을 어떻게 해야할지 생각하면 될 것 같다.
first,second, last 방법 말고, 값을 풀어서 다시 나누는 방법을 실행 해봤다.
(flatten (partition 1 4 (flatten [[:x :e :e] [:o :x :e] [:o :e :x]])))
;; (:x :x :x)
반대 대각선은 이렇게 하면 될 것 같다.
(flatten (partition 1 4 (flatten (map #(reverse %) [[:x :e :o] [:x :o :e] [:o :e :x]]))))
;; (:o :o :o)
(fn [coll]
(let [a (flatten (partition 1 4 (flatten coll)))
r (flatten (partition 1 4 (flatten (map #(reverse %) coll))))
b (into (into (into coll (apply map vector coll)) [a]) [r])]
(cond (some true? (map #(every? (fn [x] (= x :x)) %) b)) :x
(some true? (map #(every? (fn [x] (= x :o)) %) b)) :o
:else nil)))
이번 문제를 풀어보면서 중요한 사실을 알게 된 것이 있는데 어떤 데이터 형식만을 고집하기 보다는 그 형식을 풀어서 문제는 다른 각도로 생각해보는 것도 필요하다는 것을 알게 되었다
아래와 같은 형식으로도 some을 변경이 가능하다.
(fn [coll]
(let [a (flatten (partition 1 4 (flatten coll)))
r (flatten (partition 1 4 (flatten (map #(reverse %) coll))))
b (into (into (into coll (apply map vector coll)) [a]) [r])]
(some {[:x :x :x] :x [:o :o :o] :o} b)))