Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

game: chess #15

Merged
merged 28 commits into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8cda4dd
chore: copy a bunch of files from chess-rs
raklaptudirm Sep 11, 2024
852935f
chore: better chess move encoding
raklaptudirm Sep 12, 2024
cd38ce3
chore: some fixes
raklaptudirm Sep 13, 2024
c08c120
chore: hopefully working after_move
raklaptudirm Sep 13, 2024
6af74c7
chore: some semblence of a movegen
raklaptudirm Sep 14, 2024
e4f077d
chore: fix universe bb
raklaptudirm Sep 14, 2024
7d447c5
AHHHHHHHH
raklaptudirm Sep 14, 2024
a7f4b30
chore: fix a bunch of tables and shit
raklaptudirm Sep 25, 2024
6cf043d
a bunch of fixes; finally perft without crashes
raklaptudirm Nov 27, 2024
da33046
chore: add double push support
raklaptudirm Nov 27, 2024
ff45155
chore: movegen promotions
raklaptudirm Nov 27, 2024
e6f2b44
chore: remove some debug stuff
raklaptudirm Nov 27, 2024
2c3169d
chore: macros for creating Set types
raklaptudirm Dec 6, 2024
9808ee5
chore: fix some warnings
raklaptudirm Dec 6, 2024
fc11a2a
news: rak the idiot strikes again
raklaptudirm Dec 6, 2024
8c0d5aa
chore: remove some debug stuff
raklaptudirm Dec 6, 2024
25ab5cd
chore: start with all the rights
raklaptudirm Dec 6, 2024
fdceb8b
chore: don't bulk count if the split flag is set
raklaptudirm Dec 8, 2024
7edceed
chore: almost complete en passant movegen
raklaptudirm Dec 8, 2024
344f430
chore: detect attacks from kings
raklaptudirm Dec 8, 2024
3a00ecd
chore: parse castling rights from fen
raklaptudirm Dec 10, 2024
d4d4f96
chore: ep pawn double pin fix
raklaptudirm Dec 10, 2024
556baa2
chore: deeper perft tests
raklaptudirm Dec 10, 2024
66d1774
chore: some cleanup stuff
raklaptudirm Dec 12, 2024
299a5e8
chore: updated config
raklaptudirm Dec 12, 2024
ebd995c
chore: new gitignore
raklaptudirm Dec 12, 2024
81fb1db
chore: actually run the tests :skull:
raklaptudirm Dec 23, 2024
54b7290
Merge branch 'master' into chess
raklaptudirm Dec 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
# update rust to latest stable version
- run: rustup update stable && rustup default stable

- run: cargo clippy # look for lint errors
- run: cargo build # look for build errors
- run: cargo test # look for test errors
- run: cargo doc # look for bad documentation
- run: cargo clippy # look for lint errors
- run: cargo build # look for build errors
- run: cargo test --workspace # look for test errors
- run: cargo doc # look for bad documentation
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ Cargo.lock
*.pdb

/mexx/
/dev/
/dev/
/src/
*.oth
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ tetka-games = { path = "./games" }
[workspace]
resolver = "2"
members = [ "uxi", "games" ]

[profile.release]
debug = true
opt-level = 3
codegen-units = 1
lto = true
2 changes: 0 additions & 2 deletions games/src/ataxx/piece.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt;
use std::ops;
use std::str::FromStr;

use crate::interface::representable_type;
use crate::interface::ColoredPieceType;
Expand Down
14 changes: 13 additions & 1 deletion games/src/ataxx/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::interface;
use crate::interface::PiecePlacementParseError;
use crate::interface::PositionType;
use crate::interface::TypeParseError;
use crate::interface::{BitBoardType, Hash, RepresentableType, SquareType};
use crate::interface::{Hash, RepresentableType, SetType, SquareType};

use thiserror::Error;

Expand Down Expand Up @@ -86,6 +86,18 @@ impl PositionType for Position {
self.bitboards[piece as usize]
}

fn side_to_move(&self) -> interface::Color<Self> {
self.side_to_move
}

fn half_move_clock(&self) -> usize {
self.half_move_clock as usize
}

fn ply_count(&self) -> usize {
self.ply_count as usize
}

fn hash(&self) -> Hash {
self.checksum
}
Expand Down
3 changes: 0 additions & 3 deletions games/src/ataxx/square.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt;
use std::str::FromStr;

use crate::interface::{representable_type, RepresentableType, SquareType};

representable_type!(
Expand Down
177 changes: 177 additions & 0 deletions games/src/chess/bitboard.rs

Large diffs are not rendered by default.

269 changes: 269 additions & 0 deletions games/src/chess/castling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
// Copyright © 2023 Rak Laptudirm <[email protected]>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::str::FromStr;

use crate::interface::{
representable_type, set_type, RepresentableType, SetType, SquareType,
TypeParseError,
};

use thiserror::Error;

use super::{BitBoard, Color, File, Rank, Square};

set_type!(Rights<Dimension>: u8);

representable_type!(
enum Dimension: u8 {
WhiteH "H", WhiteA "A",
BlackH "h", BlackA "a",
}
);

impl Dimension {
pub const N: usize = 4;

pub fn from(color: Color, side: Side) -> Dimension {
unsafe {
Dimension::unsafe_from(color as usize * Color::N + side as usize)
}
}

pub const fn color(self) -> Color {
match self {
Dimension::WhiteH | Dimension::WhiteA => Color::White,
Dimension::BlackH | Dimension::BlackA => Color::Black,
}
}

pub const fn side(self) -> Side {
match self {
Dimension::WhiteH | Dimension::BlackH => Side::H,
Dimension::WhiteA | Dimension::BlackA => Side::A,
}
}

pub fn from_sqs(king_sq: Square, rook_sq: Square) -> Dimension {
let color: Color = if king_sq.rank() == Rank::First {
Color::White
} else {
Color::Black
};

Dimension::from(color, Side::from_sqs(king_sq, rook_sq))
}

pub fn get_targets(self) -> (Square, Square) {
match self {
Dimension::WhiteH => (Square::G1, Square::F1),
Dimension::WhiteA => (Square::C1, Square::D1),
Dimension::BlackH => (Square::G8, Square::F8),
Dimension::BlackA => (Square::C8, Square::D8),
}
}
}

#[derive(Copy, Clone, PartialEq, Eq)]
#[rustfmt::skip]
pub enum Side {
H, A,
}

impl Side {
pub fn from_sqs(king_sq: Square, rook_sq: Square) -> Side {
if (king_sq as u8) < rook_sq as u8 {
Side::H
} else {
Side::A
}
}
}

#[derive(Clone)]
pub struct Info {
pub rights: Rights,
rooks: [Square; Dimension::N],
attacks_mask: [BitBoard; Dimension::N],
blocker_mask: [BitBoard; Dimension::N],
rights_masks: [Rights; Square::N],
}

mod ends {
use super::Square;

pub const WHITE_KING_H: Square = Square::G1;
pub const WHITE_KING_A: Square = Square::C1;
pub const BLACK_KING_H: Square = Square::G8;
pub const BLACK_KING_A: Square = Square::C8;

pub const WHITE_ROOK_H: Square = Square::F1;
pub const WHITE_ROOK_A: Square = Square::D1;
pub const BLACK_ROOK_H: Square = Square::F8;
pub const BLACK_ROOK_A: Square = Square::D8;
}

#[derive(Error, Debug)]
pub enum CastlingRightsParseError {
#[error("error parsing file string \"{0}\"")]
FileParseError(#[from] TypeParseError),
#[error("found invalid castling rights \"{0}\"")]
Invalid(String),
}

impl Info {
pub fn from_str(
s: &str,
white_king: Square,
black_king: Square,
) -> Result<Self, CastlingRightsParseError> {
if s == "-" {
return Ok(Info::from_squares(
Square::E1,
File::H,
File::A,
Square::E8,
File::H,
File::A,
Rights::new(),
));
}

if s.is_empty() || s.len() > 4 {
return Err(CastlingRightsParseError::Invalid(s.to_string()));
}

let frc = !matches!(
unsafe { s.chars().next().unwrap_unchecked() },
'K' | 'Q' | 'k' | 'q'
);

let mut rights = Rights::new();

let mut white_h = File::H;
let mut white_a = File::A;
let mut black_h = File::H;
let mut black_a = File::A;

for right in s.chars() {
if frc {
if right.is_uppercase() {
let file =
File::from_str(&right.to_lowercase().to_string())?;
if file as usize > white_king.file() as usize {
white_h = file;
rights = rights | Dimension::WhiteH;
} else {
white_a = file;
rights = rights | Dimension::WhiteA;
}
} else {
let file = File::from_str(&right.to_string())?;
if file as usize > black_king.file() as usize {
black_h = file;
rights = rights | Dimension::BlackH;
} else {
black_a = file;
rights = rights | Dimension::BlackA;
}
}
} else {
match right {
'K' => rights = rights | Dimension::WhiteH,
'Q' => rights = rights | Dimension::WhiteA,
'k' => rights = rights | Dimension::BlackH,
'q' => rights = rights | Dimension::BlackA,
_ => {
return Err(CastlingRightsParseError::Invalid(
right.to_string(),
))
}
}
}
}

Ok(Info::from_squares(
white_king, white_h, white_a, black_king, black_h, black_a, rights,
))
}

#[rustfmt::skip]
pub fn from_squares(
w_king: Square, w_rook_h: File, w_rook_a: File,
b_king: Square, b_rook_h: File, b_rook_a: File,
rights: Rights,
) -> Info {
let mut info = Info {
rights,
rooks: [Square::A1; Dimension::N],
attacks_mask: [BitBoard::EMPTY; Dimension::N],
blocker_mask: [BitBoard::EMPTY; Dimension::N],
rights_masks: [Rights::new(); Square::N],
};

// Get the bit offsets/indexes of each side-color.
let wh = Dimension::WhiteH as usize;
let wa = Dimension::WhiteA as usize;
let bh = Dimension::BlackH as usize;
let ba = Dimension::BlackA as usize;

// Initialize the rook square table.
info.rooks[wh] = Square::new(w_rook_h, Rank::First);
info.rooks[wa] = Square::new(w_rook_a, Rank::First);
info.rooks[bh] = Square::new(b_rook_h, Rank::Eighth);
info.rooks[ba] = Square::new(b_rook_a, Rank::Eighth);

// Initialize the castling path table.
info.blocker_mask[wh] = blocker_mask(w_king, info.rooks[wh], ends::WHITE_KING_H, ends::WHITE_ROOK_H);
info.blocker_mask[wa] = blocker_mask(w_king, info.rooks[wa], ends::WHITE_KING_A, ends::WHITE_ROOK_A);
info.blocker_mask[bh] = blocker_mask(b_king, info.rooks[bh], ends::BLACK_KING_H, ends::BLACK_ROOK_H);
info.blocker_mask[ba] = blocker_mask(b_king, info.rooks[ba], ends::BLACK_KING_A, ends::BLACK_ROOK_A);

info.attacks_mask[wh] = BitBoard::between2(w_king, ends::WHITE_KING_H);
info.attacks_mask[wa] = BitBoard::between2(w_king, ends::WHITE_KING_A);
info.attacks_mask[bh] = BitBoard::between2(b_king, ends::BLACK_KING_H);
info.attacks_mask[ba] = BitBoard::between2(b_king, ends::BLACK_KING_A);

fn blocker_mask(king: Square, rook: Square, king_end: Square, rook_end: Square) -> BitBoard {
(BitBoard::between2(king, king_end) | BitBoard::between2(rook, rook_end)) - (BitBoard::from(king) | BitBoard::from(rook))
}

// Initialize the rights update for the king's squares.
info.rights_masks[w_king as usize] = Rights::new() | Dimension::WhiteH | Dimension::WhiteA;
info.rights_masks[b_king as usize] = Rights::new() | Dimension::BlackH | Dimension::BlackA;

// Initialize the rights update for the rook's squares.
info.rights_masks[info.rooks[wh] as usize] = Rights::new() | Dimension::WhiteH;
info.rights_masks[info.rooks[wa] as usize] = Rights::new() | Dimension::WhiteA;
info.rights_masks[info.rooks[bh] as usize] = Rights::new() | Dimension::BlackH;
info.rights_masks[info.rooks[ba] as usize] = Rights::new() | Dimension::BlackA;

info
}

pub fn get_updates(&self, square: Square) -> Rights {
self.rights_masks[square as usize]
}

pub fn rook(&self, side: Dimension) -> Square {
self.rooks[side as usize]
}

pub fn attack_mask(&self, side: Dimension) -> BitBoard {
self.attacks_mask[side as usize]
}

pub fn blocker_mask(&self, side: Dimension) -> BitBoard {
self.blocker_mask[side as usize]
}
}
Loading
Loading