From 5f23960621238ce6492976c2e12df1b11324fe20 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 30 May 2021 09:48:30 +0200 Subject: [PATCH 01/16] Inital commit --- src/chess_game/core.clj | 2 +- src/chess_game/engine/board.clj | 10 +++++++--- src/chess_game/engine/moves.clj | 27 +++++++++++++++++++++++++++ src/chess_game/engine/util.clj | 15 +++++++++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 src/chess_game/engine/moves.clj create mode 100644 src/chess_game/engine/util.clj diff --git a/src/chess_game/core.clj b/src/chess_game/core.clj index 760996b..c45198d 100644 --- a/src/chess_game/core.clj +++ b/src/chess_game/core.clj @@ -5,4 +5,4 @@ (defn -main "I don't do a whole lot ... yet." [& args] - (println (-> (board/starting-board) board/bit->board))) + (println (-> (board/starting-board)))) diff --git a/src/chess_game/engine/board.clj b/src/chess_game/engine/board.clj index ac96b37..c0e3543 100644 --- a/src/chess_game/engine/board.clj +++ b/src/chess_game/engine/board.clj @@ -4,6 +4,10 @@ (def boards (edn/read-string (slurp (io/resource "boards.edn")))) +(defn bit-set-at-pos? [bit-board pos] + (-> (unsigned-bit-shift-right bit-board pos) (bit-and 1) (= 1))) + + (defn board->bit "Converts a vector-board into its bit-board representation. @@ -32,9 +36,9 @@ which one will be printed. It depends on the order it is taken from the bit-board map." [bbit] - (let [piece-at-pos? #(-> (bbit %) (unsigned-bit-shift-right %2) (bit-and 1) (= 1))] + (let [piece-at-pos? #(-> (bbit %) (bit-set-at-pos? %2))] (vec (for [pos (take 64 (iterate dec 63)) - :let [piece-at-pos (filterv #(piece-at-pos? % pos) (keys bbit))] + :let [piece-at-pos (filterv #(piece-at-pos? % pos) (vals bbit))] :let [piece (if (empty? piece-at-pos) :- (piece-at-pos 0))]] piece)))) @@ -55,4 +59,4 @@ (newline)) (defn starting-board [] - (board->bit (:initial-setup boards))) \ No newline at end of file + (board->bit (:initial-setup boards))) diff --git a/src/chess_game/engine/moves.clj b/src/chess_game/engine/moves.clj new file mode 100644 index 0000000..ce981d6 --- /dev/null +++ b/src/chess_game/engine/moves.clj @@ -0,0 +1,27 @@ +(ns chess-game.engine.moves + (:require [chess-game.engine.util :as util])) + +(def north 8) +(def northeast 9) +(def east 1) +(def southeast -7) +(def south -8) +(def southwest -9) +(def west -1) +(def northwest 7) +(def notAFile 0xfefefefefefefefe) +(def notHFile 0x7f7f7f7f7f7f7f7f) + +(defn generalized-shift [bboard shift] + "Shifts left for positive amounts, but right for negative amounts." + (if (> shift 0) + (bit-shift-left bboard shift) + (bit-shift-right bboard shift))) + +(defn single-pawn-push-targets [pawn-push] ) + +(defn possible-pawn-moves :white + [history bit-boards start-pos color] + (let [pawn-bboard (:p bit-boards) + empty-bboard (:non-occupied bit-boards) + push-per-side (util/intersection pawn-bboard (generalized-shift empty-bboard north))])) diff --git a/src/chess_game/engine/util.clj b/src/chess_game/engine/util.clj new file mode 100644 index 0000000..dfa4709 --- /dev/null +++ b/src/chess_game/engine/util.clj @@ -0,0 +1,15 @@ +(ns chess-game.engine.util) + +;; Bitwise operations + +(defn most-significant-bit [bboard] + + ) + +(defn intersection [bboard1 bboard2] + "Intersection of two bitboards" + (bit-and bboard1 bboard2)) + +(defn union [bboard1 bboard2] + "Union of two bitboards" + (bit-or bboard1 bboard2)) From 4b6c345f570decc8dbfaeeee68dd6a3694c493f5 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 26 Jul 2021 18:10:52 +0200 Subject: [PATCH 02/16] Add incomplete white king moves --- src/chess_game/engine/moves.clj | 121 +++++++++++++++++++++++++------- 1 file changed, 97 insertions(+), 24 deletions(-) diff --git a/src/chess_game/engine/moves.clj b/src/chess_game/engine/moves.clj index ce981d6..2eaeba6 100644 --- a/src/chess_game/engine/moves.clj +++ b/src/chess_game/engine/moves.clj @@ -1,27 +1,100 @@ (ns chess-game.engine.moves (:require [chess-game.engine.util :as util])) -(def north 8) -(def northeast 9) -(def east 1) -(def southeast -7) -(def south -8) -(def southwest -9) -(def west -1) -(def northwest 7) -(def notAFile 0xfefefefefefefefe) -(def notHFile 0x7f7f7f7f7f7f7f7f) - -(defn generalized-shift [bboard shift] - "Shifts left for positive amounts, but right for negative amounts." - (if (> shift 0) - (bit-shift-left bboard shift) - (bit-shift-right bboard shift))) - -(defn single-pawn-push-targets [pawn-push] ) - -(defn possible-pawn-moves :white - [history bit-boards start-pos color] - (let [pawn-bboard (:p bit-boards) - empty-bboard (:non-occupied bit-boards) - push-per-side (util/intersection pawn-bboard (generalized-shift empty-bboard north))])) +(def not-A-file 0xfefefefefefefefe) +(def not-H-file 0x7f7f7f7f7f7f7f7f) + +;;Directions +;; northwest north northeast +;; +9 +8 +7 +;; \ | / +;; west 1 <- 0 -> -1 east +;; / | \ +;; -7 -8 -9 +;; southwest south southeast + +;;Bit-shift-left is for positive directions. Right is for negative directions +(defn north-one [bitboard] + (bit-shift-left bitboard 8)) + +(defn south-one [bitboard] + (bit-shift-right bitboard 8)) + +(defn east-one [bitboard] + (bit-shift-right bitboard 1)) + +(defn west-one [bitboard] + (bit-shift-left bitboard 1)) + +(defn north-east-one [bitboard] + (bit-shift-right bitboard 9)) + +(defn south-east-one [bitboard] + (bit-shift-left bitboard 7)) + +(defn north-west-one [bitboard] + (bit-shift-left bitboard 9)) + +(defn south-west-one [bitboard] + (bit-shift-right bitboard 7)) + +(defn white-possible-pawn-moves + [history bitboards] + (let [white-pawns-bboard (get-in bitboards [:white :P]) + black-pieces-bboard (get-in bitboards [:occupancy :black]) + + ;;White attack diagonal right + attacks-ne (bit-and (north-east-one white-pawns-bboard) black-pieces-bboard not-A-file) + + ;;White attack diagonal left + attacks-nw (bit-and (north-west-one white-pawns-bboard) black-pieces-bboard not-H-file) + + ;;White push single + + ;;White push double + + ])) + +(defn white-king-moves [bitboards] + ;;White king can move in 8 different directions + ; Numbers indicate the number of shifts for a given position + ; + ; + ; 7 8 9 + ; -1 K 1 + ; -9 -8 -7 + ; + ; + ; + (let [white-king-bboard (get-in bitboards [:white :K]) + white-pieces-bboard (get-in bitboards [:occupancy :white]) + black-pieces-bboard (get-in bitboards [:occupancy :black]) + + ;;Clears board if king is on either the A or H. + ;;Directions on the opposite site are still needed though + ;;Therefore we make two separate variables. + king-clip-file-A-bboard (bit-and white-king-bboard not-A-file) + king-clip-file-H-bboard (bit-and white-king-bboard not-H-file) + + + ;;Moves affected if the king occupies the A-file + move-ne (bit-and (north-east-one white-king-bboard) king-clip-file-A-bboard) + move-nw (bit-and (north-west-one white-king-bboard) king-clip-file-A-bboard) + move-w (bit-and (west-one white-king-bboard) king-clip-file-A-bboard) + + ;;Moves affected if the king occupies the H-file + move-sw (bit-and (south-west-one white-king-bboard) king-clip-file-H-bboard) + move-se (bit-and (south-east-one white-king-bboard) king-clip-file-H-bboard) + move-e (bit-and (east-one white-king-bboard) king-clip-file-H-bboard) + + ;;Moves not affected by either A or H-file + move-n (north-one white-king-bboard) + move-s (south-one white-king-bboard) + + ;;Union all possible moves and remove moves where white already has pieces + king-moves (bit-and (bit-or move-ne move-nw move-w + move-sw move-se move-e + move-n move-s) white-pieces-bboard) + + + ])) From 7aa88d400e1d9ba305ceb943b0e6205f520c8b2e Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 26 Jul 2021 18:12:42 +0200 Subject: [PATCH 03/16] Add comments --- src/chess_game/engine/moves.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/chess_game/engine/moves.clj b/src/chess_game/engine/moves.clj index 2eaeba6..025eec3 100644 --- a/src/chess_game/engine/moves.clj +++ b/src/chess_game/engine/moves.clj @@ -96,5 +96,7 @@ move-sw move-se move-e move-n move-s) white-pieces-bboard) + ;;Remove moves that will result in check. - ])) + ] + king-moves)) From 8afad330ec0364750782a5ece3df921a3b0a7982 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 26 Jul 2021 22:15:44 +0200 Subject: [PATCH 04/16] Fix issues with king moves - Still issue with 64th position --- src/chess_game/engine/moves.clj | 129 +++++++++++++++++++------- src/chess_game/engine/util.clj | 15 --- test/chess_game/engine/moves_test.clj | 37 ++++++++ 3 files changed, 131 insertions(+), 50 deletions(-) delete mode 100644 src/chess_game/engine/util.clj create mode 100644 test/chess_game/engine/moves_test.clj diff --git a/src/chess_game/engine/moves.clj b/src/chess_game/engine/moves.clj index 025eec3..c0adffd 100644 --- a/src/chess_game/engine/moves.clj +++ b/src/chess_game/engine/moves.clj @@ -1,8 +1,11 @@ -(ns chess-game.engine.moves - (:require [chess-game.engine.util :as util])) +(ns chess-game.engine.moves) -(def not-A-file 0xfefefefefefefefe) -(def not-H-file 0x7f7f7f7f7f7f7f7f) +"Bitboard where all bits are 1 except for the given file. + Used to clip moves, so they wont jump to the other side of the board" +(def not-A-file (unchecked-long 0x7f7f7f7f7f7f7f7f)) +(def not-H-file (unchecked-long 0xfefefefefefefefe)) +(def not-G-file (unchecked-long 0xfdfdfdfdfdfdfdfd)) ;; Used for knights +(def not-B-file (unchecked-long 0xbfbfbfbfbfbfbfbf)) ;; Used for knights ;;Directions ;; northwest north northeast @@ -15,28 +18,28 @@ ;;Bit-shift-left is for positive directions. Right is for negative directions (defn north-one [bitboard] - (bit-shift-left bitboard 8)) + (bit-shift-left (unchecked-long bitboard) (unchecked-long 8))) (defn south-one [bitboard] - (bit-shift-right bitboard 8)) + (bit-shift-right (unchecked-long bitboard) (unchecked-long 8))) (defn east-one [bitboard] - (bit-shift-right bitboard 1)) + (bit-shift-right (unchecked-long bitboard) (unchecked-long 1))) (defn west-one [bitboard] - (bit-shift-left bitboard 1)) + (bit-shift-left (unchecked-long bitboard) (unchecked-long 1))) (defn north-east-one [bitboard] - (bit-shift-right bitboard 9)) + (bit-shift-left (unchecked-long bitboard) (unchecked-long 7))) (defn south-east-one [bitboard] - (bit-shift-left bitboard 7)) + (bit-shift-right (unchecked-long bitboard) (unchecked-long 9))) (defn north-west-one [bitboard] - (bit-shift-left bitboard 9)) + (bit-shift-left (unchecked-long bitboard) (unchecked-long 9))) (defn south-west-one [bitboard] - (bit-shift-right bitboard 7)) + (bit-shift-right (unchecked-long bitboard) (unchecked-long 7))) (defn white-possible-pawn-moves [history bitboards] @@ -56,47 +59,103 @@ ])) (defn white-king-moves [bitboards] - ;;White king can move in 8 different directions - ; Numbers indicate the number of shifts for a given position - ; - ; - ; 7 8 9 - ; -1 K 1 - ; -9 -8 -7 - ; - ; - ; + "White king can move in 8 different directions + + + 1 2 3 + 8 K 4 + 7 6 5 + + + " (let [white-king-bboard (get-in bitboards [:white :K]) white-pieces-bboard (get-in bitboards [:occupancy :white]) black-pieces-bboard (get-in bitboards [:occupancy :black]) - - ;;Clears board if king is on either the A or H. + _ (println white-king-bboard) + _ (println not-A-file) + _ (println not-H-file) + ;;Clips board if king is on either the A or H. ;;Directions on the opposite site are still needed though ;;Therefore we make two separate variables. king-clip-file-A-bboard (bit-and white-king-bboard not-A-file) + _ (println king-clip-file-A-bboard) king-clip-file-H-bboard (bit-and white-king-bboard not-H-file) + _ (println king-clip-file-H-bboard) - ;;Moves affected if the king occupies the A-file - move-ne (bit-and (north-east-one white-king-bboard) king-clip-file-A-bboard) - move-nw (bit-and (north-west-one white-king-bboard) king-clip-file-A-bboard) - move-w (bit-and (west-one white-king-bboard) king-clip-file-A-bboard) + move-ne (north-east-one king-clip-file-H-bboard) + _ (println move-ne) + move-nw (north-west-one king-clip-file-A-bboard) + _ (println move-nw) + move-w (west-one king-clip-file-A-bboard) + _ (println move-w) - ;;Moves affected if the king occupies the H-file - move-sw (bit-and (south-west-one white-king-bboard) king-clip-file-H-bboard) - move-se (bit-and (south-east-one white-king-bboard) king-clip-file-H-bboard) - move-e (bit-and (east-one white-king-bboard) king-clip-file-H-bboard) + move-sw (south-west-one king-clip-file-A-bboard) + _ (println move-sw) + move-se (south-east-one king-clip-file-H-bboard) + _ (println move-se) + move-e (east-one king-clip-file-H-bboard) + - (println move-e) ;;Moves not affected by either A or H-file move-n (north-one white-king-bboard) + _ (println move-n) move-s (south-one white-king-bboard) + _ (println move-s) - ;;Union all possible moves and remove moves where white already has pieces - king-moves (bit-and (bit-or move-ne move-nw move-w + _ (println (bit-or move-ne move-nw move-w move-sw move-se move-e - move-n move-s) white-pieces-bboard) + move-n move-s)) + ;;Union all possible moves and remove moves where white already has pieces + king-moves (bit-and (bit-not white-pieces-bboard) (bit-or move-ne move-nw move-w + move-sw move-se move-e + move-n move-s)) ;;Remove moves that will result in check. ] king-moves)) + + + + + +;;Knight directions +; noNoWe noNoEa +; +17 +15 +; | | +;noWeWe +10__| |__+6 noEaEa +; \ / +; >0< +; __ / \ __ +;soWeWe -6 | | -10 soEaEa +; | | +; -15 -17 +; soSoWe soSoEa + +(defn white-knight-moves [bitboards] + " + + 2 3 + 1 4 + N + 8 5 + 7 6 + + + " + (let [white-knight-bboard (get-in bitboards [:white :K]) + white-pieces-bboard (get-in bitboards [:occupancy :white]) + black-pieces-bboard (get-in bitboards [:occupancy :black]) + + ;;Clips A,B,G and H files to make sure the knight does not jump to the other side of the board + ;;when we shift the bits + knight-clip-A-files not-A-file + knight-clip-H-files not-H-file + knight-clip-A-B-files (bit-and not-A-file not-B-file) + knight-clip-H-B-files (bit-and not-H-file not-B-file) + knight-clip-A-G-files (bit-and not-A-file not-G-file) + knight-clip-H-G-files (bit-and not-H-file not-G-file) + + ]) + ) diff --git a/src/chess_game/engine/util.clj b/src/chess_game/engine/util.clj deleted file mode 100644 index dfa4709..0000000 --- a/src/chess_game/engine/util.clj +++ /dev/null @@ -1,15 +0,0 @@ -(ns chess-game.engine.util) - -;; Bitwise operations - -(defn most-significant-bit [bboard] - - ) - -(defn intersection [bboard1 bboard2] - "Intersection of two bitboards" - (bit-and bboard1 bboard2)) - -(defn union [bboard1 bboard2] - "Union of two bitboards" - (bit-or bboard1 bboard2)) diff --git a/test/chess_game/engine/moves_test.clj b/test/chess_game/engine/moves_test.clj new file mode 100644 index 0000000..a02e122 --- /dev/null +++ b/test/chess_game/engine/moves_test.clj @@ -0,0 +1,37 @@ +(ns chess-game.engine.moves-test + (:require [clojure.test :refer :all] + [chess-game.engine.moves :as move])) + + +(deftest king-moves + "Validating king moves from different positions" + (testing "Testing king pos H4" + (is (= (move/white-king-moves {:white {:K 16777216} + :occupancy {:white 1 + :black 2048}}) + 12918652928))) + + (testing "King position H1 (First bit)" + (is (= (move/white-king-moves {:white {:K (unchecked-long 1)} + :occupancy {:white 0 + :black 2048}}) + (unchecked-long 770)))) + + (testing "King position A1" + (is (= (move/white-king-moves {:white {:K (unchecked-long 128)} + :occupancy {:white 0 + :black 2048}}) + (unchecked-long 49216)))) + + (testing "King position A8 (Last bit)" + (is (= (move/white-king-moves {:white {:K (unchecked-long 72057594037927936)} + :occupancy {:white 0 + :black 0}}) + (unchecked-long 144959613005987840)))) + + (testing "King position A8 (Last bit)" + (is (= (move/white-king-moves {:white {:K (unchecked-long 9223372036854775808)} + :occupancy {:white 1 + :black 2048}}) + (unchecked-long 4665729213955833856)))) + ) From d0a2c0ee47f8ef7d03b37295d617b148806f1def Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 26 Jul 2021 23:06:43 +0200 Subject: [PATCH 05/16] Fix issues with king moves at pos 64 --- src/chess_game/engine/moves.clj | 54 +++++++++++---------------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/src/chess_game/engine/moves.clj b/src/chess_game/engine/moves.clj index c0adffd..34f6eef 100644 --- a/src/chess_game/engine/moves.clj +++ b/src/chess_game/engine/moves.clj @@ -21,10 +21,10 @@ (bit-shift-left (unchecked-long bitboard) (unchecked-long 8))) (defn south-one [bitboard] - (bit-shift-right (unchecked-long bitboard) (unchecked-long 8))) + (unsigned-bit-shift-right (unchecked-long bitboard) (unchecked-long 8))) (defn east-one [bitboard] - (bit-shift-right (unchecked-long bitboard) (unchecked-long 1))) + (unsigned-bit-shift-right (unchecked-long bitboard) (unchecked-long 1))) (defn west-one [bitboard] (bit-shift-left (unchecked-long bitboard) (unchecked-long 1))) @@ -33,13 +33,13 @@ (bit-shift-left (unchecked-long bitboard) (unchecked-long 7))) (defn south-east-one [bitboard] - (bit-shift-right (unchecked-long bitboard) (unchecked-long 9))) + (unsigned-bit-shift-right (unchecked-long bitboard) (unchecked-long 9))) (defn north-west-one [bitboard] (bit-shift-left (unchecked-long bitboard) (unchecked-long 9))) (defn south-west-one [bitboard] - (bit-shift-right (unchecked-long bitboard) (unchecked-long 7))) + (unsigned-bit-shift-right (unchecked-long bitboard) (unchecked-long 7))) (defn white-possible-pawn-moves [history bitboards] @@ -71,48 +71,28 @@ (let [white-king-bboard (get-in bitboards [:white :K]) white-pieces-bboard (get-in bitboards [:occupancy :white]) black-pieces-bboard (get-in bitboards [:occupancy :black]) - _ (println white-king-bboard) - _ (println not-A-file) - _ (println not-H-file) - ;;Clips board if king is on either the A or H. - ;;Directions on the opposite site are still needed though - ;;Therefore we make two separate variables. - king-clip-file-A-bboard (bit-and white-king-bboard not-A-file) - _ (println king-clip-file-A-bboard) - king-clip-file-H-bboard (bit-and white-king-bboard not-H-file) - _ (println king-clip-file-H-bboard) - - - move-ne (north-east-one king-clip-file-H-bboard) - _ (println move-ne) - move-nw (north-west-one king-clip-file-A-bboard) - _ (println move-nw) - move-w (west-one king-clip-file-A-bboard) - _ (println move-w) - - move-sw (south-west-one king-clip-file-A-bboard) - _ (println move-sw) - move-se (south-east-one king-clip-file-H-bboard) - _ (println move-se) - move-e (east-one king-clip-file-H-bboard) - - (println move-e) - - ;;Moves not affected by either A or H-file + + ;;Masks the board if the king is on either the A or H. + ;;If the king is on A, we don't want to calculate positions to the left and vice versa. + king-mask-file-A-bboard (bit-and white-king-bboard not-A-file) + king-mask-file-H-bboard (bit-and white-king-bboard not-H-file) + + ;Sets a bit in all 8 direction if possible + move-ne (north-east-one king-mask-file-H-bboard) move-n (north-one white-king-bboard) - _ (println move-n) + move-nw (north-west-one king-mask-file-A-bboard) + move-w (west-one king-mask-file-A-bboard) + move-sw (south-west-one king-mask-file-A-bboard) move-s (south-one white-king-bboard) - _ (println move-s) + move-se (south-east-one king-mask-file-H-bboard) + move-e (east-one king-mask-file-H-bboard) - _ (println (bit-or move-ne move-nw move-w - move-sw move-se move-e - move-n move-s)) ;;Union all possible moves and remove moves where white already has pieces king-moves (bit-and (bit-not white-pieces-bboard) (bit-or move-ne move-nw move-w move-sw move-se move-e move-n move-s)) ;;Remove moves that will result in check. - ] king-moves)) From ca3311a7d864544c9d33ab80c7f890d0549e503b Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 27 Jul 2021 14:04:53 +0200 Subject: [PATCH 06/16] Add function to get all possible moves by knights --- src/chess_game/engine/direction.clj | 72 ++++++++++++++ src/chess_game/engine/moves.clj | 133 +++++++++----------------- test/chess_game/engine/moves_test.clj | 43 ++++++++- 3 files changed, 156 insertions(+), 92 deletions(-) create mode 100644 src/chess_game/engine/direction.clj diff --git a/src/chess_game/engine/direction.clj b/src/chess_game/engine/direction.clj new file mode 100644 index 0000000..1690cf3 --- /dev/null +++ b/src/chess_game/engine/direction.clj @@ -0,0 +1,72 @@ +(ns chess-game.engine.direction) + +;;Directions +;; northwest north northeast +;; +9 +8 +7 +;; \ | / +;; west 1 <- 0 -> -1 east +;; / | \ +;; -7 -8 -9 +;; southwest south southeast +(defn north-one [bitboard] + (bit-shift-left (unchecked-long bitboard) 8)) + +(defn south-one [bitboard] + (unsigned-bit-shift-right (unchecked-long bitboard) 8)) + +(defn east-one [bitboard] + (unsigned-bit-shift-right (unchecked-long bitboard) 1)) + +(defn west-one [bitboard] + (bit-shift-left (unchecked-long bitboard) 1)) + +(defn north-east-one [bitboard] + (bit-shift-left (unchecked-long bitboard) 7)) + +(defn south-east-one [bitboard] + (unsigned-bit-shift-right (unchecked-long bitboard) 9)) + +(defn north-west-one [bitboard] + (bit-shift-left (unchecked-long bitboard) 9)) + +(defn south-west-one [bitboard] + (unsigned-bit-shift-right (unchecked-long bitboard) 7)) + + +; ONLY USED BY KNIGHTS +; The naming represent a move in each direction -> North north east, would be two spaces up and one to the right +; Knight directions +; noNoWe noNoEa +; +17 +15 +; | | +;noWeWe +10__| |__+6 noEaEa +; \ / +; >0< +; __ / \ __ +;soWeWe -6 | | -10 soEaEa +; | | +; -15 -17 +; soSoWe soSoEa +(defn north-north-east [bitboard] + (bit-shift-left (unchecked-long bitboard) 15)) + +(defn north-north-west [bitboard] + (bit-shift-left (unchecked-long bitboard) 17)) + +(defn north-east-east [bitboard] + (bit-shift-left (unchecked-long bitboard) 6)) + +(defn north-west-west [bitboard] + (bit-shift-left (unchecked-long bitboard) 10)) + +(defn south-west-west [bitboard] + (unsigned-bit-shift-right (unchecked-long bitboard) 6)) + +(defn south-east-east [bitboard] + (unsigned-bit-shift-right (unchecked-long bitboard) 10)) + +(defn south-south-west [bitboard] + (unsigned-bit-shift-right (unchecked-long bitboard) 15)) + +(defn south-south-east [bitboard] + (unsigned-bit-shift-right (unchecked-long bitboard) 17)) diff --git a/src/chess_game/engine/moves.clj b/src/chess_game/engine/moves.clj index 34f6eef..5445f0c 100644 --- a/src/chess_game/engine/moves.clj +++ b/src/chess_game/engine/moves.clj @@ -1,56 +1,24 @@ -(ns chess-game.engine.moves) +(ns chess-game.engine.moves + (:require [chess-game.engine.direction :as direction])) "Bitboard where all bits are 1 except for the given file. Used to clip moves, so they wont jump to the other side of the board" (def not-A-file (unchecked-long 0x7f7f7f7f7f7f7f7f)) (def not-H-file (unchecked-long 0xfefefefefefefefe)) -(def not-G-file (unchecked-long 0xfdfdfdfdfdfdfdfd)) ;; Used for knights -(def not-B-file (unchecked-long 0xbfbfbfbfbfbfbfbf)) ;; Used for knights - -;;Directions -;; northwest north northeast -;; +9 +8 +7 -;; \ | / -;; west 1 <- 0 -> -1 east -;; / | \ -;; -7 -8 -9 -;; southwest south southeast +(def not-AB-file (unchecked-long 0x3f3f3f3f3f3f3f3f)) ;; Used for knights +(def not-HG-file (unchecked-long 0xfcfcfcfcfcfcfcfc)) ;; Used for knights ;;Bit-shift-left is for positive directions. Right is for negative directions -(defn north-one [bitboard] - (bit-shift-left (unchecked-long bitboard) (unchecked-long 8))) - -(defn south-one [bitboard] - (unsigned-bit-shift-right (unchecked-long bitboard) (unchecked-long 8))) - -(defn east-one [bitboard] - (unsigned-bit-shift-right (unchecked-long bitboard) (unchecked-long 1))) - -(defn west-one [bitboard] - (bit-shift-left (unchecked-long bitboard) (unchecked-long 1))) - -(defn north-east-one [bitboard] - (bit-shift-left (unchecked-long bitboard) (unchecked-long 7))) - -(defn south-east-one [bitboard] - (unsigned-bit-shift-right (unchecked-long bitboard) (unchecked-long 9))) - -(defn north-west-one [bitboard] - (bit-shift-left (unchecked-long bitboard) (unchecked-long 9))) - -(defn south-west-one [bitboard] - (unsigned-bit-shift-right (unchecked-long bitboard) (unchecked-long 7))) - -(defn white-possible-pawn-moves +#_(defn white-possible-pawn-moves [history bitboards] (let [white-pawns-bboard (get-in bitboards [:white :P]) black-pieces-bboard (get-in bitboards [:occupancy :black]) ;;White attack diagonal right - attacks-ne (bit-and (north-east-one white-pawns-bboard) black-pieces-bboard not-A-file) + attacks-ne (bit-and (direction/north-east-one white-pawns-bboard) black-pieces-bboard not-A-file) ;;White attack diagonal left - attacks-nw (bit-and (north-west-one white-pawns-bboard) black-pieces-bboard not-H-file) + attacks-nw (bit-and (direction/north-west-one white-pawns-bboard) black-pieces-bboard not-H-file) ;;White push single @@ -62,9 +30,9 @@ "White king can move in 8 different directions - 1 2 3 - 8 K 4 - 7 6 5 + 1 2 3 + 8 K 4 + 7 6 5 " @@ -77,15 +45,15 @@ king-mask-file-A-bboard (bit-and white-king-bboard not-A-file) king-mask-file-H-bboard (bit-and white-king-bboard not-H-file) - ;Sets a bit in all 8 direction if possible - move-ne (north-east-one king-mask-file-H-bboard) - move-n (north-one white-king-bboard) - move-nw (north-west-one king-mask-file-A-bboard) - move-w (west-one king-mask-file-A-bboard) - move-sw (south-west-one king-mask-file-A-bboard) - move-s (south-one white-king-bboard) - move-se (south-east-one king-mask-file-H-bboard) - move-e (east-one king-mask-file-H-bboard) + ;Sets a bit in all 8 directions if possible + move-ne (direction/north-east-one king-mask-file-H-bboard) + move-n (direction/north-one white-king-bboard) + move-nw (direction/north-west-one king-mask-file-A-bboard) + move-w (direction/west-one king-mask-file-A-bboard) + move-sw (direction/south-west-one king-mask-file-A-bboard) + move-s (direction/south-one white-king-bboard) + move-se (direction/south-east-one king-mask-file-H-bboard) + move-e (direction/east-one king-mask-file-H-bboard) ;;Union all possible moves and remove moves where white already has pieces king-moves (bit-and (bit-not white-pieces-bboard) (bit-or move-ne move-nw move-w @@ -96,46 +64,35 @@ ] king-moves)) +(defn get-knight-moves [bitboards] + "Knights can move in 8 different directions - - - -;;Knight directions -; noNoWe noNoEa -; +17 +15 -; | | -;noWeWe +10__| |__+6 noEaEa -; \ / -; >0< -; __ / \ __ -;soWeWe -6 | | -10 soEaEa -; | | -; -15 -17 -; soSoWe soSoEa - -(defn white-knight-moves [bitboards] - " - - 2 3 - 1 4 + 2 3 + 1 4 N - 8 5 - 7 6 + 8 5 + 7 6 " - (let [white-knight-bboard (get-in bitboards [:white :K]) - white-pieces-bboard (get-in bitboards [:occupancy :white]) - black-pieces-bboard (get-in bitboards [:occupancy :black]) + (let [;;There is no distinction between black and white knight moves, + ;; therefore we only need to decide from what color we extract the bit boards + white-knight-bboard (unchecked-long (get-in bitboards [:white :N])) + white-pieces-bboard (unchecked-long (get-in bitboards [:occupancy :white])) + + ;;Calculates the position in all 8 directions + knight-no-e-e (direction/north-east-east (bit-and white-knight-bboard not-HG-file)) + knight-no-no-e (direction/north-north-east (bit-and white-knight-bboard not-H-file)) + knight-no-no-w (direction/north-north-west (bit-and white-knight-bboard not-A-file)) + knight-no-w-w (direction/north-west-west (bit-and white-knight-bboard not-AB-file)) + knight-so-w-w (direction/south-west-west (bit-and white-knight-bboard not-AB-file)) + knight-so-so-w (direction/south-south-west (bit-and white-knight-bboard not-A-file)) + knight-so-so-e (direction/south-south-east (bit-and white-knight-bboard not-H-file)) + knight-so-e-e (direction/south-east-east (bit-and white-knight-bboard not-HG-file)) - ;;Clips A,B,G and H files to make sure the knight does not jump to the other side of the board - ;;when we shift the bits - knight-clip-A-files not-A-file - knight-clip-H-files not-H-file - knight-clip-A-B-files (bit-and not-A-file not-B-file) - knight-clip-H-B-files (bit-and not-H-file not-B-file) - knight-clip-A-G-files (bit-and not-A-file not-G-file) - knight-clip-H-G-files (bit-and not-H-file not-G-file) - - ]) - ) + ;;Union all possible moves and remove moves where white already has pieces + knight-moves (bit-and (bit-not white-pieces-bboard) + (bit-or knight-no-e-e knight-no-no-e knight-no-no-w knight-no-w-w + knight-so-e-e knight-so-so-e knight-so-so-w knight-so-w-w)) + ] + knight-moves)) diff --git a/test/chess_game/engine/moves_test.clj b/test/chess_game/engine/moves_test.clj index a02e122..f83cd2b 100644 --- a/test/chess_game/engine/moves_test.clj +++ b/test/chess_game/engine/moves_test.clj @@ -23,7 +23,7 @@ :black 2048}}) (unchecked-long 49216)))) - (testing "King position A8 (Last bit)" + (testing "King position H8" (is (= (move/white-king-moves {:white {:K (unchecked-long 72057594037927936)} :occupancy {:white 0 :black 0}}) @@ -31,7 +31,42 @@ (testing "King position A8 (Last bit)" (is (= (move/white-king-moves {:white {:K (unchecked-long 9223372036854775808)} - :occupancy {:white 1 + :occupancy {:white 0 + :black 2048}}) + (unchecked-long 4665729213955833856))))) + +(deftest knight-moves + "Validating king moves from different positions" + (testing "King position H1 (First bit)" + (is (= (move/get-knight-moves {:white {:N (unchecked-long 1)} + :occupancy {:white 0 + :black 2048}}) + (unchecked-long 132096)))) + + (testing "King position A1" + (is (= (move/get-knight-moves {:white {:N (unchecked-long 128)} + :occupancy {:white 0 + :black 2048}}) + (unchecked-long 4202496)))) + + (testing "King position H8" + (is (= (move/get-knight-moves {:white {:N (unchecked-long 72057594037927936)} + :occupancy {:white 0 + :black 0}}) + (unchecked-long 1128098930098176)))) + + (testing "King position A8 (Last bit)" + (is (= (move/get-knight-moves {:white {:N (unchecked-long 9223372036854775808)} + :occupancy {:white 0 + :black 2048}}) + (unchecked-long 9077567998918656)))) + (testing "Knight position D4" + (is (= (move/get-knight-moves {:white {:N (unchecked-long 268435456)} + :occupancy {:white (unchecked-long 0) + :black 2048}}) + (unchecked-long 44272527353856)))) + (testing "Knight position D4, but all slots are filled by white pieces" + (is (= (move/get-knight-moves {:white {:N (unchecked-long 268435456)} + :occupancy {:white (unchecked-long 44272527353856) :black 2048}}) - (unchecked-long 4665729213955833856)))) - ) + (unchecked-long 0))))) From 322a2b0d75936b0f81dddcdd3a246d9216ecf828 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 27 Jul 2021 17:13:40 +0200 Subject: [PATCH 07/16] Add function to calculate both black and white pawn moves. Also add test for edgecases and additional refactoring --- src/chess_game/engine/moves.clj | 174 +++++++++++++++++++------- test/chess_game/engine/moves_test.clj | 140 +++++++++++++++------ 2 files changed, 227 insertions(+), 87 deletions(-) diff --git a/src/chess_game/engine/moves.clj b/src/chess_game/engine/moves.clj index 5445f0c..bafccaa 100644 --- a/src/chess_game/engine/moves.clj +++ b/src/chess_game/engine/moves.clj @@ -1,32 +1,100 @@ (ns chess-game.engine.moves (:require [chess-game.engine.direction :as direction])) +;;TODO +;;Rooks +;;Bishops +;;Queens +;;Promotion of pawn +;;Concept of check - and not being able to move intgvf o check as king +;;En Pessant +;;Castling + "Bitboard where all bits are 1 except for the given file. Used to clip moves, so they wont jump to the other side of the board" (def not-A-file (unchecked-long 0x7f7f7f7f7f7f7f7f)) (def not-H-file (unchecked-long 0xfefefefefefefefe)) +(def rank-3 (unchecked-long 0xff0000)) +(def rank-6 (unchecked-long 0xff0000000000)) (def not-AB-file (unchecked-long 0x3f3f3f3f3f3f3f3f)) ;; Used for knights (def not-HG-file (unchecked-long 0xfcfcfcfcfcfcfcfc)) ;; Used for knights -;;Bit-shift-left is for positive directions. Right is for negative directions -#_(defn white-possible-pawn-moves - [history bitboards] - (let [white-pawns-bboard (get-in bitboards [:white :P]) - black-pieces-bboard (get-in bitboards [:occupancy :black]) +(defn find-white-pawn-moves [white-pawn-bboard all-pieces black-pieces] + "White pawns can only move forward if there are no white and black pieces on those positions. + White pawns can only attacks north-west or north-east if there is a black piece on that position + + 2 + 3 1 4 + P + + + " + (let [empty-slots (bit-not all-pieces) + + ;;Checks if the step in front of the pawn is not occupied by other pieces + white-pawn-single-push (bit-and (direction/north-one white-pawn-bboard) empty-slots) + + ;;Checks if the pawn can move an additional step based on the results of white-pawn-single-push + ;;If the pawn is on rank 3 and the space in front is free, it can move again. + white-pawn-double-push (bit-and (direction/north-one (bit-and white-pawn-single-push rank-3)) empty-slots) + + ;;Union both to get all possible forward pawn moves + white-pawn-push (bit-or white-pawn-single-push white-pawn-double-push) ;;White attack diagonal right - attacks-ne (bit-and (direction/north-east-one white-pawns-bboard) black-pieces-bboard not-A-file) + attacks-ne (bit-and (direction/north-east-one white-pawn-bboard) not-A-file) ;;White attack diagonal left - attacks-nw (bit-and (direction/north-west-one white-pawns-bboard) black-pieces-bboard not-H-file) + attacks-nw (bit-and (direction/north-west-one white-pawn-bboard) not-H-file) + + ;;Union both to get all possible attack moves + white-attack-moves (bit-and black-pieces (bit-or attacks-ne attacks-nw)) + + ;;TODO En passant moves + ] + + ;;Union white pawn attacks and pushes + (bit-or white-pawn-push white-attack-moves))) + +(defn find-black-pawn-moves [black-pawn-bboard all-pieces white-pieces] + "Black pawns can only move forward if there are no white and black pieces on those positions. + Black pawns can only attacks north-west or north-east if there is a white piece on that position + + + P + 4 1 3 + 2 - ;;White push single - ;;White push double + " + (let [empty-slots (bit-not all-pieces) - ])) + ;;Checks if the step in front of the pawn is not occupied by other pieces + black-pawn-single-push (bit-and (direction/south-one black-pawn-bboard) empty-slots) -(defn white-king-moves [bitboards] + ;;Checks if the pawn can move an additional step based on the results of black-pawn-single-push + ;;If the pawn is on rank 3 and the space in front is free, it can move again. + black-pawn-double-push (bit-and (direction/south-one (bit-and black-pawn-single-push rank-6)) empty-slots) + + ;;Union both to get all possible forward pawn moves + white-pawn-push (bit-or black-pawn-single-push black-pawn-double-push) + + ;;Black attack diagonal right + attacks-ne (bit-and (direction/south-east-one black-pawn-bboard) not-A-file) + + ;;Black attack diagonal left + attacks-nw (bit-and (direction/south-west-one black-pawn-bboard) not-H-file) + + ;;Union both to get all possible attack moves + white-attack-moves (bit-and white-pieces (bit-or attacks-ne attacks-nw)) + + ;;TODO En passant moves + ] + + ;;Union white pawn attacks and pushes + (bit-or white-pawn-push white-attack-moves))) + +(defn find-king-moves [king-bboard own-pieces-bboard] "White king can move in 8 different directions @@ -36,35 +104,32 @@ " - (let [white-king-bboard (get-in bitboards [:white :K]) - white-pieces-bboard (get-in bitboards [:occupancy :white]) - black-pieces-bboard (get-in bitboards [:occupancy :black]) - - ;;Masks the board if the king is on either the A or H. + (let [;;Masks the board if the king is on either the A or H. ;;If the king is on A, we don't want to calculate positions to the left and vice versa. - king-mask-file-A-bboard (bit-and white-king-bboard not-A-file) - king-mask-file-H-bboard (bit-and white-king-bboard not-H-file) + king-mask-file-A-bboard (bit-and king-bboard not-A-file) + king-mask-file-H-bboard (bit-and king-bboard not-H-file) ;Sets a bit in all 8 directions if possible move-ne (direction/north-east-one king-mask-file-H-bboard) - move-n (direction/north-one white-king-bboard) + move-n (direction/north-one king-bboard) move-nw (direction/north-west-one king-mask-file-A-bboard) move-w (direction/west-one king-mask-file-A-bboard) move-sw (direction/south-west-one king-mask-file-A-bboard) - move-s (direction/south-one white-king-bboard) + move-s (direction/south-one king-bboard) move-se (direction/south-east-one king-mask-file-H-bboard) move-e (direction/east-one king-mask-file-H-bboard) ;;Union all possible moves and remove moves where white already has pieces - king-moves (bit-and (bit-not white-pieces-bboard) (bit-or move-ne move-nw move-w - move-sw move-se move-e - move-n move-s)) + king-moves (bit-and (bit-not own-pieces-bboard) (bit-or move-ne move-nw move-w + move-sw move-se move-e + move-n move-s)) + + ;;TODO Remove moves that will result in check. - ;;Remove moves that will result in check. ] king-moves)) -(defn get-knight-moves [bitboards] +(defn find-knight-moves [knight-bboard own-pieces-bboard] "Knights can move in 8 different directions 2 3 @@ -75,24 +140,41 @@ " - (let [;;There is no distinction between black and white knight moves, - ;; therefore we only need to decide from what color we extract the bit boards - white-knight-bboard (unchecked-long (get-in bitboards [:white :N])) - white-pieces-bboard (unchecked-long (get-in bitboards [:occupancy :white])) - - ;;Calculates the position in all 8 directions - knight-no-e-e (direction/north-east-east (bit-and white-knight-bboard not-HG-file)) - knight-no-no-e (direction/north-north-east (bit-and white-knight-bboard not-H-file)) - knight-no-no-w (direction/north-north-west (bit-and white-knight-bboard not-A-file)) - knight-no-w-w (direction/north-west-west (bit-and white-knight-bboard not-AB-file)) - knight-so-w-w (direction/south-west-west (bit-and white-knight-bboard not-AB-file)) - knight-so-so-w (direction/south-south-west (bit-and white-knight-bboard not-A-file)) - knight-so-so-e (direction/south-south-east (bit-and white-knight-bboard not-H-file)) - knight-so-e-e (direction/south-east-east (bit-and white-knight-bboard not-HG-file)) - - ;;Union all possible moves and remove moves where white already has pieces - knight-moves (bit-and (bit-not white-pieces-bboard) - (bit-or knight-no-e-e knight-no-no-e knight-no-no-w knight-no-w-w - knight-so-e-e knight-so-so-e knight-so-so-w knight-so-w-w)) - ] - knight-moves)) + (let [;;Calculates the position in all 8 directions + knight-no-e-e (direction/north-east-east (bit-and knight-bboard not-HG-file)) + knight-no-no-e (direction/north-north-east (bit-and knight-bboard not-H-file)) + knight-no-no-w (direction/north-north-west (bit-and knight-bboard not-A-file)) + knight-no-w-w (direction/north-west-west (bit-and knight-bboard not-AB-file)) + knight-so-w-w (direction/south-west-west (bit-and knight-bboard not-AB-file)) + knight-so-so-w (direction/south-south-west (bit-and knight-bboard not-A-file)) + knight-so-so-e (direction/south-south-east (bit-and knight-bboard not-H-file)) + knight-so-e-e (direction/south-east-east (bit-and knight-bboard not-HG-file))] + + ;;Union all possible moves and remove moves where white already has pieces + (bit-and (bit-not own-pieces-bboard) + (bit-or knight-no-e-e knight-no-no-e knight-no-no-w knight-no-w-w + knight-so-e-e knight-so-so-e knight-so-so-w knight-so-w-w)))) + +(defn white-turn? [bitboards] + (get-in bitboards [:history :turn])) + + +(defn get-moves [bitboards] + "Just a temp function to illustrate that the king and knight functions are generic and works for both colors + This is not true for the pawn functions." + (if (white-turn? bitboards) + (let [occupancy (:occupancy bitboards) + white-pieces (:white occupancy) + + king-moves (find-king-moves (get-in bitboards [:white :K]) white-pieces) + knight-moves (find-knight-moves (get-in bitboards [:white :N]) white-pieces) + pawn-moves (find-white-pawn-moves (get-in bitboards [:white :P]) (:all occupancy) (:black occupancy))] + (bit-or king-moves knight-moves pawn-moves)) + (let [occupancy (:occupancy bitboards) + black-pieces (:black occupancy) + + king-moves (find-king-moves (get-in bitboards [:black :k]) black-pieces) + knight-moves (find-knight-moves (get-in bitboards [:black :n]) black-pieces) + pawn-moves (find-white-pawn-moves (get-in bitboards [:black :p]) (:all occupancy) (:white occupancy))] + (bit-or king-moves knight-moves pawn-moves))) + ) diff --git a/test/chess_game/engine/moves_test.clj b/test/chess_game/engine/moves_test.clj index f83cd2b..1edcd2f 100644 --- a/test/chess_game/engine/moves_test.clj +++ b/test/chess_game/engine/moves_test.clj @@ -3,70 +3,128 @@ [chess-game.engine.moves :as move])) -(deftest king-moves +(deftest king-moves-test "Validating king moves from different positions" (testing "Testing king pos H4" - (is (= (move/white-king-moves {:white {:K 16777216} - :occupancy {:white 1 - :black 2048}}) + (is (= (move/find-king-moves 16777216 1) 12918652928))) (testing "King position H1 (First bit)" - (is (= (move/white-king-moves {:white {:K (unchecked-long 1)} - :occupancy {:white 0 - :black 2048}}) + (is (= (move/find-king-moves (unchecked-long 1) 0) (unchecked-long 770)))) (testing "King position A1" - (is (= (move/white-king-moves {:white {:K (unchecked-long 128)} - :occupancy {:white 0 - :black 2048}}) + (is (= (move/find-king-moves (unchecked-long 128) 0) (unchecked-long 49216)))) (testing "King position H8" - (is (= (move/white-king-moves {:white {:K (unchecked-long 72057594037927936)} - :occupancy {:white 0 - :black 0}}) + (is (= (move/find-king-moves (unchecked-long 72057594037927936) 0) (unchecked-long 144959613005987840)))) (testing "King position A8 (Last bit)" - (is (= (move/white-king-moves {:white {:K (unchecked-long 9223372036854775808)} - :occupancy {:white 0 - :black 2048}}) + (is (= (move/find-king-moves (unchecked-long 9223372036854775808) 0) (unchecked-long 4665729213955833856))))) -(deftest knight-moves - "Validating king moves from different positions" - (testing "King position H1 (First bit)" - (is (= (move/get-knight-moves {:white {:N (unchecked-long 1)} - :occupancy {:white 0 - :black 2048}}) +(deftest knight-moves-test + "Validating knight moves from different positions" + (testing "Knight position H1 (First bit)" + (is (= (move/find-knight-moves (unchecked-long 1) 0) (unchecked-long 132096)))) - (testing "King position A1" - (is (= (move/get-knight-moves {:white {:N (unchecked-long 128)} - :occupancy {:white 0 - :black 2048}}) + (testing "Knight position A1" + (is (= (move/find-knight-moves (unchecked-long 128) 0) (unchecked-long 4202496)))) - (testing "King position H8" - (is (= (move/get-knight-moves {:white {:N (unchecked-long 72057594037927936)} - :occupancy {:white 0 - :black 0}}) + (testing "Knight position H8" + (is (= (move/find-knight-moves (unchecked-long 72057594037927936) 0) (unchecked-long 1128098930098176)))) - (testing "King position A8 (Last bit)" - (is (= (move/get-knight-moves {:white {:N (unchecked-long 9223372036854775808)} - :occupancy {:white 0 - :black 2048}}) + (testing "Knight position A8 (Last bit)" + (is (= (move/find-knight-moves (unchecked-long 9223372036854775808) 0) (unchecked-long 9077567998918656)))) + (testing "Knight position D4" - (is (= (move/get-knight-moves {:white {:N (unchecked-long 268435456)} - :occupancy {:white (unchecked-long 0) - :black 2048}}) + (is (= (move/find-knight-moves (unchecked-long 268435456) (unchecked-long 0)) (unchecked-long 44272527353856)))) + (testing "Knight position D4, but all slots are filled by white pieces" - (is (= (move/get-knight-moves {:white {:N (unchecked-long 268435456)} - :occupancy {:white (unchecked-long 44272527353856) - :black 2048}}) - (unchecked-long 0))))) + (is (= (move/find-knight-moves (unchecked-long 268435456) (unchecked-long 44272527353856)) + (unchecked-long 0)))) + + (testing "Knight at all position D4, A1, H1, H8, A8" + (is (= (move/find-knight-moves (unchecked-long 9295429631161139329) 0) + (unchecked-long 10249939456502784))))) + +(deftest white-pawn-moves-test + (testing "initial pawn position - single and double push" + (is (= (move/find-white-pawn-moves (unchecked-long 65280) (unchecked-long 65280) 0) + (unchecked-long 4294901760)))) + + (testing "pawn push on rank other rank than 2" + (is (= (move/find-white-pawn-moves (unchecked-long 16711680) (unchecked-long 16711680) 0) + (unchecked-long 4278190080)))) + + (testing "Pawn position h2 is unable to attack black piece on a2" + (is (= (move/find-white-pawn-moves (unchecked-long 256) (unchecked-long 33024) 32768) + (unchecked-long 16842752)))) + + (testing "Pawn position h2 is able to attack black piece on g3" + (is (= (move/find-white-pawn-moves (unchecked-long 256) (unchecked-long 131328) 131072) + (unchecked-long 16973824)))) + + (testing "Pawn position a2 is unable to attack black piece on h4" + (is (= (move/find-white-pawn-moves (unchecked-long 32768) (unchecked-long 16809984) 16777216) + (unchecked-long 2155872256)))) + + (testing "Pawn position a2 is able to attack black piece on b3" + (is (= (move/find-white-pawn-moves (unchecked-long 32768) (unchecked-long 4227072) 4194304) + (unchecked-long 2160066560)))) + + (testing "Initial pawn position - pawn can only move 1 if piece is in the way on rank 4" + (is (= (move/find-white-pawn-moves (unchecked-long 16384) (unchecked-long 1073758208) 0) + (unchecked-long 4194304)))) + + (testing "Initial pawn position - pawn cannot move since piece is in the way on rank 3" + (is (= (move/find-white-pawn-moves (unchecked-long 16384) (unchecked-long 4210688) 0) + (unchecked-long 0)))) + + (testing "Pawn position b2 can attack black pieces on both diagonals" + (is (= (move/find-white-pawn-moves (unchecked-long 16384) (unchecked-long 10502144) 10485760) + (unchecked-long 1088421888))))) + +(deftest black-pawn-moves-test + (testing "initial pawn position - single and double push" + (is (= (move/find-black-pawn-moves (unchecked-long 71776119061217280) (unchecked-long 71776119061217280) 0) + (unchecked-long 281470681743360)))) + + (testing "pawn push on rank other rank than 7" + (is (= (move/find-black-pawn-moves (unchecked-long 280375465082880) (unchecked-long 280375465082880) 0) + (unchecked-long 1095216660480)))) + + (testing "Pawn position h7 is unable to attack black piece on a7" + (is (= (move/find-black-pawn-moves (unchecked-long 281474976710656) (unchecked-long 36310271995674624) (unchecked-long 36028797018963968)) + (unchecked-long 1103806595072)))) + + (testing "Pawn position h2 is able to attack black piece on g6" + (is (= (move/find-black-pawn-moves (unchecked-long 281474976710656) (unchecked-long 283673999966208) (unchecked-long 2199023255552)) + (unchecked-long 3302829850624)))) + + (testing "Pawn position a7 is unable to attack black piece on h5" + (is (= (move/find-black-pawn-moves (unchecked-long 36028797018963968) (unchecked-long 36028801313931264) 4294967296) + (unchecked-long 141287244169216)))) + + (testing "Pawn position a7 is able to attack black piece on b6" + (is (= (move/find-black-pawn-moves (unchecked-long 36028797018963968) (unchecked-long 36099165763141632) (unchecked-long 70368744177664)) + (unchecked-long 211655988346880)))) + + (testing "Initial pawn position - pawn can only move 1 if piece is in the way on rank 4" + (is (= (move/find-black-pawn-moves (unchecked-long 18014398509481984) (unchecked-long 18014673387388928) 0) + (unchecked-long 70368744177664)))) + + (testing "Initial pawn position - pawn cannot move since piece is in the way on rank 3" + (is (= (move/find-black-pawn-moves (unchecked-long 18014398509481984) (unchecked-long 18084767253659648) 0) + (unchecked-long 0)))) + + (testing "Pawn position b7 can attack black pieces on both diagonals" + (is (= (move/find-black-pawn-moves (unchecked-long 18014398509481984) (unchecked-long 18190320369926144) 175921860444160) + (unchecked-long 246565482528768))))) From 7f1d610160713c9c029b46705b1a83f7034e25ca Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 4 Sep 2021 12:07:20 +0200 Subject: [PATCH 08/16] Rename mask to clips --- src/chess_game/engine/moves.clj | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/chess_game/engine/moves.clj b/src/chess_game/engine/moves.clj index bafccaa..470ddd1 100644 --- a/src/chess_game/engine/moves.clj +++ b/src/chess_game/engine/moves.clj @@ -34,7 +34,7 @@ ;;Checks if the step in front of the pawn is not occupied by other pieces white-pawn-single-push (bit-and (direction/north-one white-pawn-bboard) empty-slots) - ;;Checks if the pawn can move an additional step based on the results of white-pawn-single-push + ;;Checks if the pawn can move an additional step using white-pawn-single-push ;;If the pawn is on rank 3 and the space in front is free, it can move again. white-pawn-double-push (bit-and (direction/north-one (bit-and white-pawn-single-push rank-3)) empty-slots) @@ -104,20 +104,20 @@ " - (let [;;Masks the board if the king is on either the A or H. + (let [;;Uses a clip if the king is on either the A or H file. ;;If the king is on A, we don't want to calculate positions to the left and vice versa. - king-mask-file-A-bboard (bit-and king-bboard not-A-file) - king-mask-file-H-bboard (bit-and king-bboard not-H-file) + king-clip-file-A-bboard (bit-and king-bboard not-A-file) + king-clip-file-H-bboard (bit-and king-bboard not-H-file) ;Sets a bit in all 8 directions if possible - move-ne (direction/north-east-one king-mask-file-H-bboard) + move-ne (direction/north-east-one king-clip-file-H-bboard) move-n (direction/north-one king-bboard) - move-nw (direction/north-west-one king-mask-file-A-bboard) - move-w (direction/west-one king-mask-file-A-bboard) - move-sw (direction/south-west-one king-mask-file-A-bboard) + move-nw (direction/north-west-one king-clip-file-A-bboard) + move-w (direction/west-one king-clip-file-A-bboard) + move-sw (direction/south-west-one king-clip-file-A-bboard) move-s (direction/south-one king-bboard) - move-se (direction/south-east-one king-mask-file-H-bboard) - move-e (direction/east-one king-mask-file-H-bboard) + move-se (direction/south-east-one king-clip-file-H-bboard) + move-e (direction/east-one king-clip-file-H-bboard) ;;Union all possible moves and remove moves where white already has pieces king-moves (bit-and (bit-not own-pieces-bboard) (bit-or move-ne move-nw move-w From d7c5d61af5ab223d7c4664a19e2dbfa4c53ec55b Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 3 Oct 2021 00:32:23 +0200 Subject: [PATCH 09/16] Add rook move calculation --- src/chess_game/engine/moves.clj | 53 +++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/chess_game/engine/moves.clj b/src/chess_game/engine/moves.clj index 470ddd1..821ea5c 100644 --- a/src/chess_game/engine/moves.clj +++ b/src/chess_game/engine/moves.clj @@ -18,6 +18,24 @@ (def rank-6 (unchecked-long 0xff0000000000)) (def not-AB-file (unchecked-long 0x3f3f3f3f3f3f3f3f)) ;; Used for knights (def not-HG-file (unchecked-long 0xfcfcfcfcfcfcfcfc)) ;; Used for knights +(def file-masks {0 (unchecked-long 0X101010101010101) ; H + 1 (unchecked-long 0X202020202020202) ; G + 2 (unchecked-long 0X404040404040404) ; F + 3 (unchecked-long 0X808080808080808) ; E + 4 (unchecked-long 0X1010101010101010) ; D + 5 (unchecked-long 0X2020202020202020) ; C + 6 (unchecked-long 0X4040404040404040) ; B + 7 (unchecked-long 0X8080808080808080)}) +(def rank-masks {0 (unchecked-long 0Xff) ; 1 + 1 (unchecked-long 0Xff00) ; 2 + 2 (unchecked-long 0Xff0000) ; 3 + 3 (unchecked-long 0Xff000000) ; 4 + 4 (unchecked-long 0Xff00000000) ; 5 + 5 (unchecked-long 0Xff0000000000) ; 6 + 6 (unchecked-long 0Xff000000000000) ; 7 + 7 (unchecked-long 0Xff00000000000000)}) +(def full-board-mask (unchecked-long 0xffffffffffffffff)) + (defn find-white-pawn-moves [white-pawn-bboard all-pieces black-pieces] "White pawns can only move forward if there are no white and black pieces on those positions. @@ -155,6 +173,38 @@ (bit-or knight-no-e-e knight-no-no-e knight-no-no-w knight-no-w-w knight-so-e-e knight-so-so-e knight-so-so-w knight-so-w-w)))) +"Hyperbola Quintessence +for further understanding: https://www.youtube.com/watch?v=bCH4YK6oq8M + +" + +(defn reverse-bits [l] + (Long/reverse l)) + +(defn calculate-ray-attacks [slider-piece occupancy] + (->> slider-piece + (* 2) + (- occupancy))) + +(defn calculate-ray-moves [slider-piece occupancy rank-file] + (bit-and + (bit-xor (calculate-ray-attacks slider-piece (bit-and occupancy rank-file)) + (reverse-bits + (calculate-ray-attacks (reverse-bits slider-piece) (reverse-bits (bit-and occupancy rank-file))))) + rank-file)) + +(defn find-rook-moves [rook-piece own-piecs occupancy] + "Hyperbola Quintessence using the o^(o-2r) trick + o^(o-2r) https://www.chessprogramming.org/Subtracting_a_Rook_from_a_Blocking_Piece + Can only take one rook-piece + Returns a bitboard with all possible moves + " + (let [rook-piece-pos (Long/numberOfTrailingZeros rook-piece) + horisontal-attacks (calculate-ray-moves rook-piece occupancy (get rank-masks (quot rook-piece-pos 8))) + vertical-attacks (calculate-ray-moves rook-piece occupancy (get file-masks (mod rook-piece-pos 8)))] + (bit-and (bit-or horisontal-attacks vertical-attacks) + (bit-not own-piecs)))) + (defn white-turn? [bitboards] (get-in bitboards [:history :turn])) @@ -176,5 +226,4 @@ king-moves (find-king-moves (get-in bitboards [:black :k]) black-pieces) knight-moves (find-knight-moves (get-in bitboards [:black :n]) black-pieces) pawn-moves (find-white-pawn-moves (get-in bitboards [:black :p]) (:all occupancy) (:white occupancy))] - (bit-or king-moves knight-moves pawn-moves))) - ) + (bit-or king-moves knight-moves pawn-moves)))) From 0bb547d44ff1739d6c2ad7016a243dec0f79c4ad Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 3 Oct 2021 00:33:20 +0200 Subject: [PATCH 10/16] Fix tests with own-piece positions for kings and knights --- test/chess_game/engine/moves_test.clj | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/chess_game/engine/moves_test.clj b/test/chess_game/engine/moves_test.clj index 1edcd2f..f91adc2 100644 --- a/test/chess_game/engine/moves_test.clj +++ b/test/chess_game/engine/moves_test.clj @@ -6,53 +6,53 @@ (deftest king-moves-test "Validating king moves from different positions" (testing "Testing king pos H4" - (is (= (move/find-king-moves 16777216 1) + (is (= (move/find-king-moves 16777216 16777216) 12918652928))) (testing "King position H1 (First bit)" - (is (= (move/find-king-moves (unchecked-long 1) 0) + (is (= (move/find-king-moves (unchecked-long 1) 1) (unchecked-long 770)))) (testing "King position A1" - (is (= (move/find-king-moves (unchecked-long 128) 0) + (is (= (move/find-king-moves (unchecked-long 128) 128) (unchecked-long 49216)))) (testing "King position H8" - (is (= (move/find-king-moves (unchecked-long 72057594037927936) 0) + (is (= (move/find-king-moves (unchecked-long 72057594037927936) (unchecked-long 72057594037927936)) (unchecked-long 144959613005987840)))) (testing "King position A8 (Last bit)" - (is (= (move/find-king-moves (unchecked-long 9223372036854775808) 0) + (is (= (move/find-king-moves (unchecked-long 9223372036854775808) (unchecked-long 9223372036854775808)) (unchecked-long 4665729213955833856))))) (deftest knight-moves-test "Validating knight moves from different positions" (testing "Knight position H1 (First bit)" - (is (= (move/find-knight-moves (unchecked-long 1) 0) + (is (= (move/find-knight-moves (unchecked-long 1) 1) (unchecked-long 132096)))) (testing "Knight position A1" - (is (= (move/find-knight-moves (unchecked-long 128) 0) + (is (= (move/find-knight-moves (unchecked-long 128) 128) (unchecked-long 4202496)))) (testing "Knight position H8" - (is (= (move/find-knight-moves (unchecked-long 72057594037927936) 0) + (is (= (move/find-knight-moves (unchecked-long 72057594037927936) (unchecked-long 72057594037927936)) (unchecked-long 1128098930098176)))) (testing "Knight position A8 (Last bit)" - (is (= (move/find-knight-moves (unchecked-long 9223372036854775808) 0) + (is (= (move/find-knight-moves (unchecked-long 9223372036854775808) (unchecked-long 9223372036854775808)) (unchecked-long 9077567998918656)))) (testing "Knight position D4" - (is (= (move/find-knight-moves (unchecked-long 268435456) (unchecked-long 0)) + (is (= (move/find-knight-moves (unchecked-long 268435456) (unchecked-long 268435456)) (unchecked-long 44272527353856)))) (testing "Knight position D4, but all slots are filled by white pieces" - (is (= (move/find-knight-moves (unchecked-long 268435456) (unchecked-long 44272527353856)) + (is (= (move/find-knight-moves (unchecked-long 268435456) (unchecked-long 44272795789312)) (unchecked-long 0)))) (testing "Knight at all position D4, A1, H1, H8, A8" - (is (= (move/find-knight-moves (unchecked-long 9295429631161139329) 0) + (is (= (move/find-knight-moves (unchecked-long 9295429631161139329) 9295429631161139329) (unchecked-long 10249939456502784))))) (deftest white-pawn-moves-test From 090ab70d7160195fdce6560aeefa59a63e5e9e9c Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 3 Oct 2021 01:39:02 +0200 Subject: [PATCH 11/16] Adjust docstring and wrap longs to avoid overflow --- src/chess_game/engine/moves.clj | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/chess_game/engine/moves.clj b/src/chess_game/engine/moves.clj index 821ea5c..506c27d 100644 --- a/src/chess_game/engine/moves.clj +++ b/src/chess_game/engine/moves.clj @@ -182,9 +182,10 @@ for further understanding: https://www.youtube.com/watch?v=bCH4YK6oq8M (Long/reverse l)) (defn calculate-ray-attacks [slider-piece occupancy] - (->> slider-piece - (* 2) - (- occupancy))) + (->> (unchecked-long slider-piece) + (*' (unchecked-long 2)) + (- occupancy) + unchecked-long)) ;Safety measure to avoid overflow when reversing bits later (defn calculate-ray-moves [slider-piece occupancy rank-file] (bit-and @@ -193,17 +194,21 @@ for further understanding: https://www.youtube.com/watch?v=bCH4YK6oq8M (calculate-ray-attacks (reverse-bits slider-piece) (reverse-bits (bit-and occupancy rank-file))))) rank-file)) -(defn find-rook-moves [rook-piece own-piecs occupancy] +(defn find-rook-moves [^Integer rook-piece-pos own-pieces occupancy] "Hyperbola Quintessence using the o^(o-2r) trick - o^(o-2r) https://www.chessprogramming.org/Subtracting_a_Rook_from_a_Blocking_Piece - Can only take one rook-piece - Returns a bitboard with all possible moves - " - (let [rook-piece-pos (Long/numberOfTrailingZeros rook-piece) - horisontal-attacks (calculate-ray-moves rook-piece occupancy (get rank-masks (quot rook-piece-pos 8))) - vertical-attacks (calculate-ray-moves rook-piece occupancy (get file-masks (mod rook-piece-pos 8)))] + o^(o-2r) : https://www.chessprogramming.org/Subtracting_a_Rook_from_a_Blocking_Piece + Hyperbola Quintessence: https://www.chessprogramming.org/Hyperbola_Quintessence + + BE AWARE: This function can only take one rook-piece at the time. So to avoid giving multiple rooks, the function does not accept + bitboards, but instead a position of a single rook eg. number of trailing zeros. + Returns a bitboard with all possible moves for specified pos" + (let [rook-piece-bitboard (unchecked-long (bit-shift-left 1 rook-piece-pos)) + _ (println rook-piece-bitboard) + horisontal-attacks (calculate-ray-moves rook-piece-bitboard occupancy (get rank-masks (quot rook-piece-pos 8))) + vertical-attacks (calculate-ray-moves rook-piece-bitboard occupancy (get file-masks (mod rook-piece-pos 8)))] + (bit-and (bit-or horisontal-attacks vertical-attacks) - (bit-not own-piecs)))) + (bit-not own-pieces)))) (defn white-turn? [bitboards] (get-in bitboards [:history :turn])) From e5d12da386cd2367b3f6a003faaf55851eecbd1c Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 3 Oct 2021 01:42:33 +0200 Subject: [PATCH 12/16] Add rook move generation tests --- test/chess_game/engine/moves_test.clj | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/chess_game/engine/moves_test.clj b/test/chess_game/engine/moves_test.clj index f91adc2..90cf63d 100644 --- a/test/chess_game/engine/moves_test.clj +++ b/test/chess_game/engine/moves_test.clj @@ -128,3 +128,16 @@ (testing "Pawn position b7 can attack black pieces on both diagonals" (is (= (move/find-black-pawn-moves (unchecked-long 18014398509481984) (unchecked-long 18190320369926144) 175921860444160) (unchecked-long 246565482528768))))) + +(deftest rook-moves-test + (testing "rook E5 with opponent piece on G5, B5 and E7 and own piece at E2" + (is (= (move/find-rook-moves 35 2056 (unchecked-long 578712869944690696)) + (unchecked-long 2261102847590400)))) + + (testing "Initial rook position H1 - blocked in corner by own pieces" + (is (= (move/find-rook-moves 0 259 259) + (unchecked-long 0)))) + + (testing "Initial rook position H1 - No blockers" + (is (= (move/find-rook-moves 0 0 0) + (unchecked-long 72340172838076926))))) From be00a97bbb26a2fc6f71114eba9bd724bc013cdc Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 3 Oct 2021 01:55:11 +0200 Subject: [PATCH 13/16] Fix test issues --- src/chess_game/engine/moves.clj | 1 - test/chess_game/engine/moves_test.clj | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/chess_game/engine/moves.clj b/src/chess_game/engine/moves.clj index 506c27d..f731622 100644 --- a/src/chess_game/engine/moves.clj +++ b/src/chess_game/engine/moves.clj @@ -203,7 +203,6 @@ for further understanding: https://www.youtube.com/watch?v=bCH4YK6oq8M bitboards, but instead a position of a single rook eg. number of trailing zeros. Returns a bitboard with all possible moves for specified pos" (let [rook-piece-bitboard (unchecked-long (bit-shift-left 1 rook-piece-pos)) - _ (println rook-piece-bitboard) horisontal-attacks (calculate-ray-moves rook-piece-bitboard occupancy (get rank-masks (quot rook-piece-pos 8))) vertical-attacks (calculate-ray-moves rook-piece-bitboard occupancy (get file-masks (mod rook-piece-pos 8)))] diff --git a/test/chess_game/engine/moves_test.clj b/test/chess_game/engine/moves_test.clj index 90cf63d..c60d007 100644 --- a/test/chess_game/engine/moves_test.clj +++ b/test/chess_game/engine/moves_test.clj @@ -52,7 +52,7 @@ (unchecked-long 0)))) (testing "Knight at all position D4, A1, H1, H8, A8" - (is (= (move/find-knight-moves (unchecked-long 9295429631161139329) 9295429631161139329) + (is (= (move/find-knight-moves (unchecked-long 9295429631161139329) (unchecked-long 9295429631161139329)) (unchecked-long 10249939456502784))))) (deftest white-pawn-moves-test From 53e2b2844d69daadb56660d0ab6da6c86e84ff9b Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 3 Oct 2021 02:03:51 +0200 Subject: [PATCH 14/16] Add docstrings --- src/chess_game/engine/moves.clj | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/chess_game/engine/moves.clj b/src/chess_game/engine/moves.clj index f731622..876ae1b 100644 --- a/src/chess_game/engine/moves.clj +++ b/src/chess_game/engine/moves.clj @@ -178,16 +178,19 @@ for further understanding: https://www.youtube.com/watch?v=bCH4YK6oq8M " -(defn reverse-bits [l] +(defn- reverse-bits [l] (Long/reverse l)) -(defn calculate-ray-attacks [slider-piece occupancy] +(defn- calculate-ray-attacks [slider-piece occupancy] + "Applying (o-2s) of o^(o-2s)" (->> (unchecked-long slider-piece) (*' (unchecked-long 2)) (- occupancy) - unchecked-long)) ;Safety measure to avoid overflow when reversing bits later + unchecked-long)) ;Safety measure to avoid overflow when reversing bits later -(defn calculate-ray-moves [slider-piece occupancy rank-file] +(defn- calculate-ray-moves [slider-piece occupancy rank-file] + "Applying o^(o-2s) ^ o^(o'-2s')' - ' symbol represents reverse bits to calculate the other direction + Can be reduced to (o-2s) ^ (o'-2s')' " (bit-and (bit-xor (calculate-ray-attacks slider-piece (bit-and occupancy rank-file)) (reverse-bits From 9537a3270b443d110230dccb77013414bbad284e Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 3 Oct 2021 15:25:23 +0200 Subject: [PATCH 15/16] Implement bishop move lookup functionality --- src/chess_game/engine/moves.clj | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/chess_game/engine/moves.clj b/src/chess_game/engine/moves.clj index 876ae1b..dbd2324 100644 --- a/src/chess_game/engine/moves.clj +++ b/src/chess_game/engine/moves.clj @@ -34,6 +34,37 @@ 5 (unchecked-long 0Xff0000000000) ; 6 6 (unchecked-long 0Xff000000000000) ; 7 7 (unchecked-long 0Xff00000000000000)}) +(def diagonal-masks {0 (unchecked-long 0x1) + 1 (unchecked-long 0x102) + 2 (unchecked-long 0x10204) + 3 (unchecked-long 0x1020408) + 4 (unchecked-long 0x102040810) + 5 (unchecked-long 0x10204081020) + 6 (unchecked-long 0x1020408102040) + 7 (unchecked-long 0x102040810204080) + 8 (unchecked-long 0x204081020408000) + 9 (unchecked-long 0x408102040800000) + 10 (unchecked-long 0x810204080000000) + 11 (unchecked-long 0x1020408000000000) + 12 (unchecked-long 0x2040800000000000) + 13 (unchecked-long 0x4080000000000000) + 14 (unchecked-long 0x8000000000000000)}) +(def anti-diagonal-masks {0 (unchecked-long 0X80) + 1 (unchecked-long 0X8040) + 2 (unchecked-long 0X804020) + 3 (unchecked-long 0X80402010) + 4 (unchecked-long 0X8040201008) + 5 (unchecked-long 0x804020100804) + 6 (unchecked-long 0x80402010080402) + 7 (unchecked-long 0x8040201008040201) + 8 (unchecked-long 0x4020100804020100) + 9 (unchecked-long 0x2010080402010000) + 10 (unchecked-long 0x1008040201000000) + 11 (unchecked-long 0x804020100000000) + 12 (unchecked-long 0x402010000000000) + 13 (unchecked-long 0x201000000000000) + 14 (unchecked-long 0x100000000000000)}) + (def full-board-mask (unchecked-long 0xffffffffffffffff)) @@ -212,6 +243,14 @@ for further understanding: https://www.youtube.com/watch?v=bCH4YK6oq8M (bit-and (bit-or horisontal-attacks vertical-attacks) (bit-not own-pieces)))) +(defn find-bishop-moves [^Integer bishop-piece-pos own-pieces occupancy] + "Using same technique as rooks" + (let [bishop-piece-bitboard (unchecked-long (bit-shift-left 1 bishop-piece-pos)) + diagonal-attacks (calculate-ray-moves bishop-piece-bitboard occupancy (get diagonal-masks (+ (quot bishop-piece-pos 8) (mod bishop-piece-pos 8)))) + anti-diagonal-attacks (calculate-ray-moves bishop-piece-bitboard occupancy (get anti-diagonal-masks (- (+ 7 (quot bishop-piece-pos 8)) (mod bishop-piece-pos 8))))] + (bit-and (bit-or diagonal-attacks anti-diagonal-attacks) + (bit-not own-pieces)))) + (defn white-turn? [bitboards] (get-in bitboards [:history :turn])) From ab51531dc4e14995d750fbe4df806c16a709f964 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 3 Oct 2021 15:25:52 +0200 Subject: [PATCH 16/16] Add bishop lookup tests --- test/chess_game/engine/moves_test.clj | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/chess_game/engine/moves_test.clj b/test/chess_game/engine/moves_test.clj index c60d007..09607cf 100644 --- a/test/chess_game/engine/moves_test.clj +++ b/test/chess_game/engine/moves_test.clj @@ -2,7 +2,6 @@ (:require [clojure.test :refer :all] [chess-game.engine.moves :as move])) - (deftest king-moves-test "Validating king moves from different positions" (testing "Testing king pos H4" @@ -141,3 +140,16 @@ (testing "Initial rook position H1 - No blockers" (is (= (move/find-rook-moves 0 0 0) (unchecked-long 72340172838076926))))) + +(deftest bishop-moves-test + (testing "Bishop E4 with opponent piece on D3, D5, F3 and F5" + (is (= (move/find-bishop-moves 27 0x8000000 (unchecked-long 0x1408140000)) + (unchecked-long 0x1400140000)))) + + (testing "Bishop E4 - No blockers" + (is (= (move/find-bishop-moves 27 0x8000000 0x8000000) + (unchecked-long 0x8041221400142241)))) + + (testing "Bishop E4 blocked by own pieces on D3, D5, F3 and F5" + (is (= (move/find-bishop-moves 27 (unchecked-long 0x1408140000) (unchecked-long 0x1408140000)) + (unchecked-long 0)))))