Fix bugs in server-side move validation.

This commit is contained in:
yukirij 2024-10-12 11:18:58 -07:00
parent d0bad35995
commit 36cc9f7543
6 changed files with 234 additions and 171 deletions

View File

@ -12,7 +12,7 @@ impl CheckState {
pub fn immediate(mut self) -> Self pub fn immediate(mut self) -> Self
{ {
self.data |= 0x80; self.data |= 0x80 * (self.data > 0) as u8;
self self
} }

View File

@ -46,6 +46,8 @@ impl Play {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct PlayInfo { pub struct PlayInfo {
pub valid:bool,
pub threat:bool,
pub play:Play, pub play:Play,
pub check:CheckState, pub check:CheckState,
pub blocking:u32, pub blocking:u32,

View File

@ -50,21 +50,21 @@ impl Game {
{ {
*self = Self::new(); *self = Self::new();
self.board.init(); self.board.init();
self.update_board();
} }
pub fn apply_history(&mut self, history:&Vec<Play>) -> Result<(),()> pub fn apply_history(&mut self, history:&Vec<Play>) -> Result<(),()>
{ {
self.init(); self.init();
for play in history { for play in history {
if self.process(play).is_err() { break } if self.process(play).is_err() { break; }
} }
Ok(()) Ok(())
} }
pub fn update_board(&mut self) pub fn update_board(&mut self)
{ {
let mut player_moves = 0usize; if self.is_complete() { return; }
let current_player = (self.turn & 1) as u8;
/* /*
** Reset board meta data. ** Reset board meta data.
@ -73,7 +73,7 @@ impl Game {
// Reset columns // Reset columns
for column in &mut self.board.columns { for column in &mut self.board.columns {
column.extent = [0, 9]; column.extent = [0, 8];
column.militia = [false; 2]; column.militia = [false; 2];
} }
@ -95,20 +95,37 @@ impl Game {
*/ */
for piece in &self.board.pieces.clone() { for piece in &self.board.pieces.clone() {
if let Some(piece) = piece { if let Some(piece) = piece {
let moves = self.get_moves_data(&piece); let piece_hex = Hex::from_tile(piece.tile);
let alt_moves = self.get_alts_data(&piece);
/* /*
** Mark threats, checks, and extents. ** Apply column extent.
*/ */
for info in &moves { if piece.player == 0 {
let hex = Hex::from_tile(info.play.to); self.board.columns[piece_hex.x as usize].extent[0] = self.board.columns[piece_hex.x as usize].extent[0].max(piece_hex.y as u8);
} else {
self.board.columns[piece_hex.x as usize].extent[1] = self.board.columns[piece_hex.x as usize].extent[1].min(piece_hex.y as u8);
}
/*
** Mark column as having unpromoted militia, if present.
*/
if piece.class == PIECE_MILITIA && !piece.promoted {
self.board.columns[piece_hex.x as usize].militia[piece.player as usize] = true;
}
/*
** Mark threats, checks, and blocking.
*/
for info in &self.get_moves_data(&piece) {
/* /*
** Mark tile as threatened. ** Mark tile as threatened.
*/ */
if info.valid || info.threat {
self.board.tiles[info.play.to as usize].threat[piece.player as usize] = true; self.board.tiles[info.play.to as usize].threat[piece.player as usize] = true;
}
if info.valid {
/* /*
** Apply checks. ** Apply checks.
*/ */
@ -128,32 +145,14 @@ impl Game {
self.board.tiles[info.play.to as usize].check = true; self.board.tiles[info.play.to as usize].check = true;
} }
/*
** Apply column extent.
*/
self.board.columns[hex.x as usize].extent[piece.player as usize] = hex.y as u8;
/*
** Mark column as having unpromoted militia, if present.
*/
if piece.class == PIECE_MILITIA && !piece.promoted {
self.board.columns[hex.x as usize].militia[piece.player as usize] = true;
}
/*
** Count moves for current player.
*/
if piece.player == current_player {
player_moves += moves.len() + alt_moves.len();
}
/* /*
** Apply blocking to piece on tile, if present. ** Apply blocking to piece on tile, if present.
*/ */
if info.blocking != 0 { if info.blocking != 0 {
if let Some(piece_id) = self.board.tiles[info.play.to as usize].piece { if let Some(target_id) = self.board.tiles[info.play.to as usize].piece {
if let Some(piece) = &mut self.board.pieces[piece_id as usize] { if let Some(target) = &mut self.board.pieces[target_id as usize] {
piece.blocking = info.blocking; target.blocking = info.blocking;
}
} }
} }
} }
@ -164,21 +163,48 @@ impl Game {
/* /*
** Determine if game is in checkmate. ** Determine if game is in checkmate.
*/ */
if player_moves == 0 { if self.get_plays().len() == 0 {
self.status = GameStatus::Checkmate; self.status = GameStatus::Checkmate;
} }
} }
pub fn get_plays(&mut self) -> Vec<Play>
{
let mut plays = Vec::new();
let current_player = (self.turn & 1) as u8;
/*
** Fill board meta data; count piece moves.
*/
for piece in &self.board.pieces.clone() {
if let Some(piece) = piece {
if piece.player == current_player {
plays.append(&mut self.get_moves(&piece));
plays.append(&mut self.get_alts(&piece));
}
}
}
/*
** Add drops to player moves.
*/
for i in 0..6 {
plays.append(&mut self.get_drops(&Piece::new(i, current_player)));
}
plays
}
pub fn process(&mut self, play:&Play) -> Result<(),()> pub fn process(&mut self, play:&Play) -> Result<(),()>
{ {
let player = (self.turn & 1) as u8; let player = (self.turn & 1) as u8;
if self.play_is_valid(play) { if self.play_is_valid(play) {
// Move piece on board. // Move piece on board.
if match play.source { match play.source {
0 | 2 => { 0 | 2 => {
if let Some(pid) = self.board.tiles[play.from as usize].piece { if let Some(piece_id) = self.board.tiles[play.from as usize].piece {
if let Some(mut piece) = self.board.pieces[pid as usize] { if let Some(mut piece) = self.board.pieces[piece_id as usize] {
let mut swap = false; let mut swap = false;
if let Some(tid) = self.board.tiles[play.to as usize].piece { if let Some(tid) = self.board.tiles[play.to as usize].piece {
@ -206,62 +232,59 @@ impl Game {
if !swap { self.board.pieces[tid as usize] = None; } if !swap { self.board.pieces[tid as usize] = None; }
} }
// Set tile/piece associations. /*
** Move piece to new tile.
*/
if swap { if swap {
self.board.tiles[play.from as usize].piece = self.board.tiles[play.to as usize].piece; self.board.tiles[play.from as usize].piece = self.board.tiles[play.to as usize].piece;
} else { } else {
self.board.tiles[play.from as usize].piece = None; self.board.tiles[play.from as usize].piece = None;
} }
self.board.tiles[play.to as usize].piece = Some(pid); self.board.tiles[play.to as usize].piece = Some(piece_id);
piece.tile = play.to; piece.tile = play.to;
self.board.pieces[pid as usize] = Some(piece);
// Check for piece promotion. // Check for piece promotion.
let hex = Hex::from_tile(play.to); let hex = Hex::from_tile(play.to);
if !piece.promoted && piece.has_promotion() && Hex::is_back(hex.x, hex.y, piece.player) { if !piece.promoted && piece.has_promotion() && Hex::is_back(hex.x, hex.y, piece.player) {
if let Some(piece) = &mut self.board.pieces[pid as usize] {
piece.promoted = true; piece.promoted = true;
} }
}
self.board.pieces[piece_id as usize] = Some(piece);
self.turn += 1; self.turn += 1;
true }
} else { false } }
} else { false }
} }
// Place piece from pool. // Place piece from pool.
1 => { 1 => {
if self.pool[player as usize][play.from as usize] > 0 && self.board.tiles[play.to as usize].piece.is_none() {
self.pool[player as usize][play.from as usize] -= 1; self.pool[player as usize][play.from as usize] -= 1;
let piece = Piece::new(play.from, player); let piece = Piece::new(play.from, player);
self.board.set_piece(piece, play.to); self.board.set_piece(piece, play.to);
self.turn += 1; self.turn += 1;
true
} else { false }
} }
// Player retired. // Player retired.
0xF => { 0xF => {
self.status = GameStatus::Resign; self.status = GameStatus::Resign;
true
} }
_ => false, _ => { }
} { }
self.history.push(*play); self.history.push(*play);
self.update_board();
Ok(()) Ok(())
} else { Err(()) } } else { Err(()) }
} else { Err(()) }
} }
pub fn play_is_valid(&self, play:&Play) -> bool pub fn play_is_valid(&self, play:&Play) -> bool
// Returns whether a play may be made by the current player. // Returns whether a play may be made by the current player.
// //
{ {
//println!("play_is_valid {} {} {}", play.source, play.from, play.to); if self.is_complete() { return false; }
let mut valid = false; let mut valid = false;
let player = (self.turn & 1) as u8; let player = (self.turn & 1) as u8;
@ -270,11 +293,10 @@ impl Game {
0 => { 0 => {
if let Some(piece_id) = self.board.tiles[play.from as usize].piece { if let Some(piece_id) = self.board.tiles[play.from as usize].piece {
if let Some(piece) = &self.board.pieces[piece_id as usize] { if let Some(piece) = &self.board.pieces[piece_id as usize] {
//println!("piece {} {}", piece.class, piece.player);
if piece.player == player { if piece.player == player {
for p in self.get_moves(piece) { for p in self.get_moves_data(piece) {
if p.to == play.to { if p.play.to == play.to {
if p.valid {
valid = true; valid = true;
break; break;
} }
@ -282,8 +304,10 @@ impl Game {
} }
} }
} }
}
valid valid
} }
1 => { 1 => {
if (play.from as usize) < self.pool[player as usize].len() { if (play.from as usize) < self.pool[player as usize].len() {
if self.pool[player as usize][play.from as usize] > 0 { if self.pool[player as usize][play.from as usize] > 0 {
@ -297,11 +321,13 @@ impl Game {
} }
valid valid
} }
2 => { 2 => {
if let Some(piece_id) = self.board.tiles[play.from as usize].piece { if let Some(piece_id) = self.board.tiles[play.from as usize].piece {
if let Some(piece) = &self.board.pieces[piece_id as usize] { if let Some(piece) = &self.board.pieces[piece_id as usize] {
if piece.player == player { if piece.player == player {
for p in self.get_alts(piece) { let plays = self.get_alts(piece);
for p in plays {
if p.to == play.to { if p.to == play.to {
valid = true; valid = true;
break; break;
@ -312,7 +338,8 @@ impl Game {
} }
valid valid
} }
0xF => { true }
0xF => true,
_ => false, _ => false,
} }
} }
@ -321,13 +348,16 @@ impl Game {
{ {
let mut plays = Vec::<PlayInfo>::new(); let mut plays = Vec::<PlayInfo>::new();
let current_player = (self.turn & 1) as u8;
let moves = piece.moves(); let moves = piece.moves();
/* /*
** Get permitted move directions. ** Get permitted move directions.
*/ */
let mut directions = moves.direction; let mut directions = moves.direction;
let blocking_directions = piece.blocking | util::rotate6(piece.blocking); let mut blocking_directions = piece.blocking;
blocking_directions |= blocking_directions << 12;
if blocking_directions != 0 { if blocking_directions != 0 {
if piece.class == PIECE_HEART { if piece.class == PIECE_HEART {
directions &= !blocking_directions; directions &= !blocking_directions;
@ -336,30 +366,24 @@ impl Game {
} }
} }
//println!("dir {:b}", directions);
//println!("blk {:b}", blocking_directions);
/* /*
** Handle movable directions. ** Handle movable directions.
*/ */
while directions != 0 { while directions != 0 {
let mask_original = util::lsb(directions); let mask_original = util::lsb(directions);
let direction_original = util::ffs(mask_original);
let mut multiplier = 1; let mut multiplier = 1;
let mut mask = mask_original;
/* /*
** Shift second tile jumps to standard direction masks. ** Shift second tile jumps to standard direction masks.
*/ */
let mut mask = mask_original;
if (mask & 0xFFF) == 0 { if (mask & 0xFFF) == 0 {
mask >>= 12; mask >>= 12;
multiplier = 2; multiplier = 2;
} }
let direction = util::ffs(mask); let direction = util::ffs(mask);
//println!(" - dir {}", direction);
/* /*
** Extract stride value: ** Extract stride value:
** 00 - 1 Tile ** 00 - 1 Tile
@ -368,7 +392,7 @@ impl Game {
** 11 - Unlimited ** 11 - Unlimited
*/ */
let stride_offset: u32 = direction * 2; let stride_offset: u32 = direction * 2;
let stride = if direction < 12 { let stride = if direction_original < 12 {
match ((3 << stride_offset) & moves.stride) >> stride_offset { match ((3 << stride_offset) & moves.stride) >> stride_offset {
0 => 1, 0 => 1,
1 => 2, 1 => 2,
@ -380,29 +404,32 @@ impl Game {
rx *= multiplier; rx *= multiplier;
ry *= multiplier; ry *= multiplier;
let mut block_count = 0;
/* /*
** Step along direction up to max stride. ** Step along direction up to max stride.
*/ */
let mut block_count = 0;
let mut current_hex = Hex::from_tile(piece.tile); let mut current_hex = Hex::from_tile(piece.tile);
for stride_dist in 0..stride { for stride_dist in 0..stride {
if Hex::is_valid(current_hex.x + rx, current_hex.y + ry) { if Hex::is_valid(current_hex.x + rx, current_hex.y + ry) {
current_hex = Hex::from_hex(current_hex.x + rx, current_hex.y + ry); current_hex = Hex::from_hex(current_hex.x + rx, current_hex.y + ry);
let mut valid = block_count == 0;
let tile = self.board.tiles[current_hex.tile as usize]; let tile = self.board.tiles[current_hex.tile as usize];
let mut valid = block_count == 0;
let mut checkstate = CheckState::new(); let mut checkstate = CheckState::new();
let mut blocking = 0;
let mut threat = false;
/* /*
** If in check, move must break check. ** If player in check, move must break check.
** King may only move to non-threatened tiles. ** King may only move to non-threatened tiles.
*/ */
if piece.class == PIECE_HEART { if piece.class == PIECE_HEART {
valid = valid && !tile.threat[(piece.player == 0) as usize]; valid &= !tile.threat[(piece.player == 0) as usize];
} else { } else {
valid = valid && match self.status { if piece.player == current_player {
valid &= match self.status {
GameStatus::Check(state) => { GameStatus::Check(state) => {
state.count() == 1 && tile.check state.count() == 1 && tile.check
} }
@ -410,41 +437,67 @@ impl Game {
_ => false, _ => false,
}; };
} }
}
/* /*
** If tile is occupied, piece must be opposing or swappable. ** If tile is occupied, piece must be opposing or swappable.
*/ */
if let Some(target_id) = tile.piece { if let Some(target_id) = tile.piece {
if let Some(target) = &self.board.pieces[target_id as usize] { if let Some(target) = &self.board.pieces[target_id as usize] {
/*
** Target is same army.
*/
if target.player == piece.player { if target.player == piece.player {
/* /*
** Move is valid if piece can swap. ** Move is valid if piece can swap.
*/ */
valid = valid && stride_dist == 0 && self.can_swap(piece, mask_original, current_hex.tile); valid = valid && stride_dist == 0 && self.can_swap(piece, mask_original, current_hex.tile);
if block_count == 0 {
threat = true;
}
block_count += 2; block_count += 2;
} else { }
/* /*
** Handle capturing of king. ** Target is opposing army.
*/
else {
/*
** Find checking of king and blocking pieces.
*/ */
if target.class == PIECE_HEART { if target.class == PIECE_HEART {
match block_count {
0 => {
/*
** Mark plays in direction as checking.
*/
if stride_dist == 0 { if stride_dist == 0 {
checkstate = checkstate.direct(); checkstate = checkstate.direct();
} else { } else {
checkstate = checkstate.stride(); checkstate = checkstate.stride();
} }
for i in 1..=stride_dist {
match block_count {
0 => {
// Mark plays in direction as check.
for i in 1..stride_dist {
let index = plays.len() - i; let index = plays.len() - i;
plays[index].check = checkstate; plays[index].check = checkstate;
} }
/*
** Apply blocking to king in directions that can be reached by piece.
*/
if stride_dist < stride - 1 {
blocking = mask;
if stride_dist > 0 {
blocking |= util::rotate6(mask);
}
}
} }
1 => { 1 => {
// Mark last piece as blocking. // Mark last piece as blocking.
let index = plays.len() - 1; let index = plays.len() - 1;
plays[index].blocking = mask; plays[index].blocking = mask | util::rotate6(mask);
} }
_ => { } _ => { }
} }
@ -455,15 +508,15 @@ impl Game {
} }
} }
if valid {
plays.push(PlayInfo { plays.push(PlayInfo {
valid,
threat,
play:Play { source:0, from:piece.tile, to:current_hex.tile, }, play:Play { source:0, from:piece.tile, to:current_hex.tile, },
check:checkstate, check:checkstate.immediate(),
blocking:0, blocking:blocking,
}); });
} }
} }
}
directions &= !mask_original; directions &= !mask_original;
} }
@ -475,15 +528,20 @@ impl Game {
{ {
let mut plays = Vec::new(); let mut plays = Vec::new();
for info in self.get_moves_data(piece) { for info in self.get_moves_data(piece) {
if info.valid {
plays.push(info.play); plays.push(info.play);
} }
}
plays plays
} }
fn get_alts_data(&self, piece:&Piece) -> Vec<PlayInfo> fn get_alts_data(&self, piece:&Piece) -> Vec<PlayInfo>
{ {
let mut plays = Vec::<PlayInfo>::new(); let mut plays = Vec::new();
/*
** Get allowed target tiles.
*/
let piece_moves = piece.moves(); let piece_moves = piece.moves();
let subject_tiles = if piece.blocking == 0 { let subject_tiles = if piece.blocking == 0 {
(0..61u8).collect() (0..61u8).collect()
@ -514,7 +572,9 @@ impl Game {
tiles tiles
}; };
/*
** Filter valid tiles from allowed.
*/
if let Some(alt_mode) = piece_moves.alt { if let Some(alt_mode) = piece_moves.alt {
match alt_mode { match alt_mode {
// Knight // Knight
@ -522,6 +582,8 @@ impl Game {
for tile_id in subject_tiles { for tile_id in subject_tiles {
if self.can_drop(piece, tile_id, flags::IGNORE_CHECK) { if self.can_drop(piece, tile_id, flags::IGNORE_CHECK) {
plays.push(PlayInfo { plays.push(PlayInfo {
valid: true,
threat: false,
play: Play::from_alt(piece.tile, tile_id), play: Play::from_alt(piece.tile, tile_id),
check: CheckState::new(), check: CheckState::new(),
blocking: 0, blocking: 0,
@ -533,25 +595,27 @@ impl Game {
// Caslte // Caslte
2 => { 2 => {
for tile_id in subject_tiles { for tile_id in subject_tiles {
let hex = Hex::from_tile(piece.tile); let piece_hex = Hex::from_tile(piece.tile);
let tile_hex = Hex::from_tile(tile_id); let tile_hex = Hex::from_tile(tile_id);
let in_rear_cone = if piece.player == 0 { let in_rear_cone = if piece.player == 0 {
if tile_hex.x >= hex.x { if tile_hex.x >= piece_hex.x {
tile_hex.y <= hex.y tile_hex.y <= piece_hex.y
} else { } else {
tile_hex.y <= hex.y - (hex.x - tile_hex.x) tile_hex.y <= piece_hex.y - (piece_hex.x - tile_hex.x)
} }
} else { } else {
if tile_hex.x >= hex.x { if tile_hex.x >= piece_hex.x {
tile_hex.y >= hex.y + (tile_hex.x - hex.x) tile_hex.y >= piece_hex.y + (tile_hex.x - piece_hex.x)
} else { } else {
tile_hex.y >= hex.y tile_hex.y >= piece_hex.y
} }
}; };
if in_rear_cone && self.can_drop(piece, tile_id, flags::IGNORE_CHECK | flags::IGNORE_EXTENT) { if in_rear_cone && self.can_drop(piece, tile_id, flags::IGNORE_CHECK | flags::IGNORE_EXTENT) {
plays.push(PlayInfo { plays.push(PlayInfo {
valid: true,
threat: false,
play: Play::from_alt(piece.tile, tile_id), play: Play::from_alt(piece.tile, tile_id),
check: CheckState::new(), check: CheckState::new(),
blocking: 0, blocking: 0,
@ -569,11 +633,7 @@ impl Game {
pub fn get_alts(&self, piece:&Piece) -> Vec<Play> pub fn get_alts(&self, piece:&Piece) -> Vec<Play>
{ {
let mut plays = Vec::new(); self.get_alts_data(piece).iter().map(|info| info.play).collect()
for info in self.get_alts_data(piece) {
plays.push(info.play);
}
plays
} }
pub fn get_drops(&self, piece:&Piece) -> Vec<Play> pub fn get_drops(&self, piece:&Piece) -> Vec<Play>
@ -601,12 +661,12 @@ impl Game {
/* /*
** Target must be same color. ** Target must be same color.
*/ */
valid = valid && piece.player == target.player; valid &= piece.player == target.player;
/* /*
** Target must not be same piece. ** Target must not be same piece.
*/ */
valid = valid && !(piece.class == target.class && piece.promoted == target.promoted); valid &= !(piece.class == target.class && piece.promoted == target.promoted);
/* /*
** King may not swap onto a contested tile. ** King may not swap onto a contested tile.
@ -621,7 +681,7 @@ impl Game {
** Target must have movement in reverse direction. ** Target must have movement in reverse direction.
*/ */
let moves = target.moves().rotate().direction; let moves = target.moves().rotate().direction;
valid = valid && (mask & moves) != 0; valid &= (mask & moves) != 0;
} }
} }
valid valid
@ -632,16 +692,18 @@ impl Game {
let hex = Hex::from_tile(tile_id); let hex = Hex::from_tile(tile_id);
let tile = &self.board.tiles[tile_id as usize]; let tile = &self.board.tiles[tile_id as usize];
let mut piece = piece.clone();
piece.tile = tile_id;
/* /*
** Tile must not be occupied. ** Tile must not be occupied.
*/ */
let mut valid = tile.piece.is_none(); let mut valid = tile.piece.is_none();
/* /*
** If in check, a piece may only be dropped if check ** If in check, a piece may only be dropped onto a tile blocking check.
** is due to a multi-tile move from a single piece.
*/ */
valid = valid && match self.status { valid &= match self.status {
GameStatus::Check(state) => { GameStatus::Check(state) => {
!state.is_direct() && state.count() == 1 && tile.check !state.is_direct() && state.count() == 1 && tile.check
} }
@ -653,10 +715,10 @@ impl Game {
** A piece may not be dropped behind the first opposing piece in a column. ** A piece may not be dropped behind the first opposing piece in a column.
*/ */
if (flags & flags::IGNORE_EXTENT as u32) == 0 { if (flags & flags::IGNORE_EXTENT as u32) == 0 {
valid = valid && if piece.player == 0 { valid &= if piece.player == 0 {
hex.y < self.board.columns[hex.x as usize].extent[1] as i8 hex.y <= self.board.columns[hex.x as usize].extent[1] as i8
} else { } else {
hex.y > self.board.columns[hex.x as usize].extent[0] as i8 hex.y >= self.board.columns[hex.x as usize].extent[0] as i8
}; };
} }
@ -675,20 +737,16 @@ impl Game {
** A piece must be able to move from its drop position. ** A piece must be able to move from its drop position.
** A piece may not be dropped onto a position that puts the opposing king in check. ** A piece may not be dropped onto a position that puts the opposing king in check.
*/ */
let piece_moves = self.get_moves(piece); let piece_moves = self.get_moves_data(&piece);
if piece_moves.len() > 0 { if piece_moves.len() > 0 {
if (flags & flags::IGNORE_CHECK as u32) == 0 { if (flags & flags::IGNORE_CHECK as u32) == 0 {
for mv in piece_moves { for mv in piece_moves {
if let Some(target_id) = self.board.tiles[mv.to as usize].piece { if mv.valid && mv.check.is_check() {
if let Some(target) = self.board.pieces[target_id as usize] {
if target.class == PIECE_HEART && target.player != piece.player {
valid = false; valid = false;
break; break;
} }
} }
} }
}
}
} else { } else {
valid = false; valid = false;
} }

View File

@ -53,7 +53,7 @@ impl MoveSet {
Self { Self {
direction:rotate6(self.direction), direction:rotate6(self.direction),
stride:rotate12(self.stride), stride:rotate12(self.stride),
alt:None, alt:self.alt,
} }
} }
} }
@ -76,7 +76,7 @@ pub const PIECES :[PieceClass; PIECES_COUNT] = [
PieceClass { PieceClass {
name: "Lance", name: "Lance",
moves: MoveSet::new() moves: MoveSet::new()
.add(bit(1) | bit(5)) .add(bit(1) | bit(3) | bit(5))
.add_stride(bit(0), 3), .add_stride(bit(0), 3),
pmoves: MoveSet::new() pmoves: MoveSet::new()
.add(bit(0) | bit(3)) .add(bit(0) | bit(3))

View File

@ -9,14 +9,17 @@ pub const fn rotate6(x:u32) -> u32
const R1 :u32 = 0x0000_003F; const R1 :u32 = 0x0000_003F;
const R2 :u32 = 0x0000_0FC0; const R2 :u32 = 0x0000_0FC0;
const R3 :u32 = 0x0003_F000; const R3 :u32 = 0x0003_F000;
const R4 :u32 = 0x00FC_0000;
let a = (x & R1) << 3; let a = (x & R1) << 3;
let b = (x & R2) << 3; let b = (x & R2) << 3;
let c = (x & R3) << 3; let c = (x & R3) << 3;
let d = (x & R4) << 3;
(a & R1) | ((a >> 6) & R1) (a & R1) | ((a >> 6) & R1)
| (b & R2) | ((b >> 6) & R2) | (b & R2) | ((b >> 6) & R2)
| (c & R3) | ((c >> 6) & R3) | (c & R3) | ((c >> 6) & R3)
| (d & R4) | ((d >> 6) & R4)
} }
pub const fn rotate12(x:u32) -> u32 pub const fn rotate12(x:u32) -> u32
@ -24,8 +27,8 @@ pub const fn rotate12(x:u32) -> u32
const R1 :u32 = 0x0000_0FFF; const R1 :u32 = 0x0000_0FFF;
const R2 :u32 = 0x00FF_F000; const R2 :u32 = 0x00FF_F000;
let a = (x & R1) << 3; let a = (x & R1) << 6;
let b = (x & R2) << 3; let b = (x & R2) << 6;
(a & R1) | ((a >> 12) & R1) (a & R1) | ((a >> 12) & R1)
| (b & R2) | ((b >> 12) & R2) | (b & R2) | ((b >> 12) & R2)

View File

@ -122,7 +122,7 @@ GAME.Tile = class {
this.piece = null; this.piece = null;
this.threaten = [0, 0]; this.threaten = [0, 0];
this.checking = 0; this.checking = false;
this.hex = HEX.tile_to_hex(index); this.hex = HEX.tile_to_hex(index);
} }
@ -136,7 +136,7 @@ GAME.Tile = class {
reset() { reset() {
this.threaten = [0, 0]; this.threaten = [0, 0];
this.checking = 0; this.checking = false;
} }
}; };
@ -294,7 +294,7 @@ GAME.Game = class {
} }
if(movement.valid && movement.check != 0) { if(movement.valid && movement.check != 0) {
is_checking = true; is_checking = true;
this.board.tiles[movement.tile].checking |= movement.check; this.board.tiles[movement.tile].checking = true;
this.state.check |= movement.check; this.state.check |= movement.check;
} }
if(movement.block != 0) { if(movement.block != 0) {
@ -303,7 +303,7 @@ GAME.Game = class {
} }
if(is_checking) { if(is_checking) {
this.board.tiles[piece.tile].checking = 1; this.board.tiles[piece.tile].checking = true;
checking_pieces++; checking_pieces++;
} }
} }
@ -522,7 +522,7 @@ GAME.Game = class {
let check_count = this.state.check & 0x3F; let check_count = this.state.check & 0x3F;
if(piece.player == (this.turn & 1) && this.state.check != 0) { if(piece.player == (this.turn & 1) && this.state.check != 0) {
if(piece.piece != GAME.Const.PieceId.Heart) { if(piece.piece != GAME.Const.PieceId.Heart) {
if(tile_data.checking != 0) { if(tile_data.checking) {
if(target_id !== null) { if(target_id !== null) {
if(check_count > 1) { if(check_count > 1) {
result = false; result = false;
@ -776,7 +776,7 @@ GAME.Game = class {
let check_count = this.state.check & 0x3F; let check_count = this.state.check & 0x3F;
if(piece.player == (this.turn & 1) && this.state.check != 0) { if(piece.player == (this.turn & 1) && this.state.check != 0) {
if(check_direct == 0 && check_count == 1) { if(check_direct == 0 && check_count == 1) {
position_valid = tile.checking != 0; position_valid = tile.checking;
} else { } else {
position_valid = false; position_valid = false;
} }