Fix bugs in server-side move validation.
This commit is contained in:
parent
d0bad35995
commit
36cc9f7543
@ -12,7 +12,7 @@ impl CheckState {
|
||||
|
||||
pub fn immediate(mut self) -> Self
|
||||
{
|
||||
self.data |= 0x80;
|
||||
self.data |= 0x80 * (self.data > 0) as u8;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,8 @@ impl Play {
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PlayInfo {
|
||||
pub valid:bool,
|
||||
pub threat:bool,
|
||||
pub play:Play,
|
||||
pub check:CheckState,
|
||||
pub blocking:u32,
|
||||
|
288
game/src/lib.rs
288
game/src/lib.rs
@ -50,21 +50,21 @@ impl Game {
|
||||
{
|
||||
*self = Self::new();
|
||||
self.board.init();
|
||||
self.update_board();
|
||||
}
|
||||
|
||||
pub fn apply_history(&mut self, history:&Vec<Play>) -> Result<(),()>
|
||||
{
|
||||
self.init();
|
||||
for play in history {
|
||||
if self.process(play).is_err() { break }
|
||||
if self.process(play).is_err() { break; }
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_board(&mut self)
|
||||
{
|
||||
let mut player_moves = 0usize;
|
||||
let current_player = (self.turn & 1) as u8;
|
||||
if self.is_complete() { return; }
|
||||
|
||||
/*
|
||||
** Reset board meta data.
|
||||
@ -73,7 +73,7 @@ impl Game {
|
||||
|
||||
// Reset columns
|
||||
for column in &mut self.board.columns {
|
||||
column.extent = [0, 9];
|
||||
column.extent = [0, 8];
|
||||
column.militia = [false; 2];
|
||||
}
|
||||
|
||||
@ -95,20 +95,37 @@ impl Game {
|
||||
*/
|
||||
for piece in &self.board.pieces.clone() {
|
||||
if let Some(piece) = piece {
|
||||
let moves = self.get_moves_data(&piece);
|
||||
let alt_moves = self.get_alts_data(&piece);
|
||||
let piece_hex = Hex::from_tile(piece.tile);
|
||||
|
||||
/*
|
||||
** Mark threats, checks, and extents.
|
||||
** Apply column extent.
|
||||
*/
|
||||
for info in &moves {
|
||||
let hex = Hex::from_tile(info.play.to);
|
||||
if piece.player == 0 {
|
||||
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.
|
||||
*/
|
||||
if info.valid || info.threat {
|
||||
self.board.tiles[info.play.to as usize].threat[piece.player as usize] = true;
|
||||
}
|
||||
|
||||
if info.valid {
|
||||
/*
|
||||
** Apply checks.
|
||||
*/
|
||||
@ -128,32 +145,14 @@ impl Game {
|
||||
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.
|
||||
*/
|
||||
if info.blocking != 0 {
|
||||
if let Some(piece_id) = self.board.tiles[info.play.to as usize].piece {
|
||||
if let Some(piece) = &mut self.board.pieces[piece_id as usize] {
|
||||
piece.blocking = info.blocking;
|
||||
if let Some(target_id) = self.board.tiles[info.play.to as usize].piece {
|
||||
if let Some(target) = &mut self.board.pieces[target_id as usize] {
|
||||
target.blocking = info.blocking;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -164,21 +163,48 @@ impl Game {
|
||||
/*
|
||||
** Determine if game is in checkmate.
|
||||
*/
|
||||
if player_moves == 0 {
|
||||
if self.get_plays().len() == 0 {
|
||||
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<(),()>
|
||||
{
|
||||
let player = (self.turn & 1) as u8;
|
||||
|
||||
if self.play_is_valid(play) {
|
||||
// Move piece on board.
|
||||
if match play.source {
|
||||
match play.source {
|
||||
0 | 2 => {
|
||||
if let Some(pid) = self.board.tiles[play.from as usize].piece {
|
||||
if let Some(mut piece) = self.board.pieces[pid as usize] {
|
||||
if let Some(piece_id) = self.board.tiles[play.from as usize].piece {
|
||||
if let Some(mut piece) = self.board.pieces[piece_id as usize] {
|
||||
let mut swap = false;
|
||||
|
||||
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; }
|
||||
}
|
||||
|
||||
// Set tile/piece associations.
|
||||
/*
|
||||
** Move piece to new tile.
|
||||
*/
|
||||
if swap {
|
||||
self.board.tiles[play.from as usize].piece = self.board.tiles[play.to as usize].piece;
|
||||
} else {
|
||||
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;
|
||||
|
||||
self.board.pieces[pid as usize] = Some(piece);
|
||||
|
||||
// Check for piece promotion.
|
||||
let hex = Hex::from_tile(play.to);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
self.board.pieces[piece_id as usize] = Some(piece);
|
||||
|
||||
self.turn += 1;
|
||||
true
|
||||
} else { false }
|
||||
} else { false }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Place piece from pool.
|
||||
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;
|
||||
let piece = Piece::new(play.from, player);
|
||||
self.board.set_piece(piece, play.to);
|
||||
|
||||
self.turn += 1;
|
||||
true
|
||||
} else { false }
|
||||
}
|
||||
|
||||
// Player retired.
|
||||
0xF => {
|
||||
self.status = GameStatus::Resign;
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
} {
|
||||
_ => { }
|
||||
}
|
||||
|
||||
self.history.push(*play);
|
||||
self.update_board();
|
||||
|
||||
Ok(())
|
||||
} else { Err(()) }
|
||||
} else { Err(()) }
|
||||
}
|
||||
|
||||
pub fn play_is_valid(&self, play:&Play) -> bool
|
||||
// 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 player = (self.turn & 1) as u8;
|
||||
@ -270,11 +293,10 @@ impl Game {
|
||||
0 => {
|
||||
if let Some(piece_id) = self.board.tiles[play.from as usize].piece {
|
||||
if let Some(piece) = &self.board.pieces[piece_id as usize] {
|
||||
//println!("piece {} {}", piece.class, piece.player);
|
||||
|
||||
if piece.player == player {
|
||||
for p in self.get_moves(piece) {
|
||||
if p.to == play.to {
|
||||
for p in self.get_moves_data(piece) {
|
||||
if p.play.to == play.to {
|
||||
if p.valid {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
@ -282,8 +304,10 @@ impl Game {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
valid
|
||||
}
|
||||
|
||||
1 => {
|
||||
if (play.from as usize) < self.pool[player as usize].len() {
|
||||
if self.pool[player as usize][play.from as usize] > 0 {
|
||||
@ -297,11 +321,13 @@ impl Game {
|
||||
}
|
||||
valid
|
||||
}
|
||||
|
||||
2 => {
|
||||
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 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 {
|
||||
valid = true;
|
||||
break;
|
||||
@ -312,7 +338,8 @@ impl Game {
|
||||
}
|
||||
valid
|
||||
}
|
||||
0xF => { true }
|
||||
|
||||
0xF => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -321,13 +348,16 @@ impl Game {
|
||||
{
|
||||
let mut plays = Vec::<PlayInfo>::new();
|
||||
|
||||
let current_player = (self.turn & 1) as u8;
|
||||
let moves = piece.moves();
|
||||
|
||||
/*
|
||||
** Get permitted move directions.
|
||||
*/
|
||||
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 piece.class == PIECE_HEART {
|
||||
directions &= !blocking_directions;
|
||||
@ -336,30 +366,24 @@ impl Game {
|
||||
}
|
||||
}
|
||||
|
||||
//println!("dir {:b}", directions);
|
||||
//println!("blk {:b}", blocking_directions);
|
||||
|
||||
/*
|
||||
** Handle movable directions.
|
||||
*/
|
||||
while directions != 0 {
|
||||
let mask_original = util::lsb(directions);
|
||||
let direction_original = util::ffs(mask_original);
|
||||
let mut multiplier = 1;
|
||||
|
||||
let mut mask = mask_original;
|
||||
|
||||
/*
|
||||
** Shift second tile jumps to standard direction masks.
|
||||
*/
|
||||
let mut mask = mask_original;
|
||||
if (mask & 0xFFF) == 0 {
|
||||
mask >>= 12;
|
||||
multiplier = 2;
|
||||
}
|
||||
|
||||
let direction = util::ffs(mask);
|
||||
|
||||
//println!(" - dir {}", direction);
|
||||
|
||||
/*
|
||||
** Extract stride value:
|
||||
** 00 - 1 Tile
|
||||
@ -368,7 +392,7 @@ impl Game {
|
||||
** 11 - Unlimited
|
||||
*/
|
||||
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 {
|
||||
0 => 1,
|
||||
1 => 2,
|
||||
@ -380,29 +404,32 @@ impl Game {
|
||||
rx *= multiplier;
|
||||
ry *= multiplier;
|
||||
|
||||
let mut block_count = 0;
|
||||
|
||||
/*
|
||||
** Step along direction up to max stride.
|
||||
*/
|
||||
let mut block_count = 0;
|
||||
let mut current_hex = Hex::from_tile(piece.tile);
|
||||
for stride_dist in 0..stride {
|
||||
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);
|
||||
|
||||
let mut valid = block_count == 0;
|
||||
let tile = self.board.tiles[current_hex.tile as usize];
|
||||
|
||||
let mut valid = block_count == 0;
|
||||
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.
|
||||
*/
|
||||
|
||||
if piece.class == PIECE_HEART {
|
||||
valid = valid && !tile.threat[(piece.player == 0) as usize];
|
||||
valid &= !tile.threat[(piece.player == 0) as usize];
|
||||
} else {
|
||||
valid = valid && match self.status {
|
||||
if piece.player == current_player {
|
||||
valid &= match self.status {
|
||||
GameStatus::Check(state) => {
|
||||
state.count() == 1 && tile.check
|
||||
}
|
||||
@ -410,41 +437,67 @@ impl Game {
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If tile is occupied, piece must be opposing or swappable.
|
||||
*/
|
||||
if let Some(target_id) = tile.piece {
|
||||
if let Some(target) = &self.board.pieces[target_id as usize] {
|
||||
|
||||
/*
|
||||
** Target is same army.
|
||||
*/
|
||||
if target.player == piece.player {
|
||||
/*
|
||||
** Move is valid if piece can swap.
|
||||
*/
|
||||
valid = valid && stride_dist == 0 && self.can_swap(piece, mask_original, current_hex.tile);
|
||||
|
||||
if block_count == 0 {
|
||||
threat = true;
|
||||
}
|
||||
|
||||
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 {
|
||||
match block_count {
|
||||
0 => {
|
||||
/*
|
||||
** Mark plays in direction as checking.
|
||||
*/
|
||||
if stride_dist == 0 {
|
||||
checkstate = checkstate.direct();
|
||||
} else {
|
||||
checkstate = checkstate.stride();
|
||||
}
|
||||
|
||||
match block_count {
|
||||
0 => {
|
||||
// Mark plays in direction as check.
|
||||
for i in 1..stride_dist {
|
||||
for i in 1..=stride_dist {
|
||||
let index = plays.len() - i;
|
||||
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 => {
|
||||
// Mark last piece as blocking.
|
||||
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 {
|
||||
valid,
|
||||
threat,
|
||||
play:Play { source:0, from:piece.tile, to:current_hex.tile, },
|
||||
check:checkstate,
|
||||
blocking:0,
|
||||
check:checkstate.immediate(),
|
||||
blocking:blocking,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
directions &= !mask_original;
|
||||
}
|
||||
@ -475,15 +528,20 @@ impl Game {
|
||||
{
|
||||
let mut plays = Vec::new();
|
||||
for info in self.get_moves_data(piece) {
|
||||
if info.valid {
|
||||
plays.push(info.play);
|
||||
}
|
||||
}
|
||||
plays
|
||||
}
|
||||
|
||||
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 subject_tiles = if piece.blocking == 0 {
|
||||
(0..61u8).collect()
|
||||
@ -514,7 +572,9 @@ impl Game {
|
||||
tiles
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Filter valid tiles from allowed.
|
||||
*/
|
||||
if let Some(alt_mode) = piece_moves.alt {
|
||||
match alt_mode {
|
||||
// Knight
|
||||
@ -522,6 +582,8 @@ impl Game {
|
||||
for tile_id in subject_tiles {
|
||||
if self.can_drop(piece, tile_id, flags::IGNORE_CHECK) {
|
||||
plays.push(PlayInfo {
|
||||
valid: true,
|
||||
threat: false,
|
||||
play: Play::from_alt(piece.tile, tile_id),
|
||||
check: CheckState::new(),
|
||||
blocking: 0,
|
||||
@ -533,25 +595,27 @@ impl Game {
|
||||
// Caslte
|
||||
2 => {
|
||||
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 in_rear_cone = if piece.player == 0 {
|
||||
if tile_hex.x >= hex.x {
|
||||
tile_hex.y <= hex.y
|
||||
if tile_hex.x >= piece_hex.x {
|
||||
tile_hex.y <= piece_hex.y
|
||||
} else {
|
||||
tile_hex.y <= hex.y - (hex.x - tile_hex.x)
|
||||
tile_hex.y <= piece_hex.y - (piece_hex.x - tile_hex.x)
|
||||
}
|
||||
} else {
|
||||
if tile_hex.x >= hex.x {
|
||||
tile_hex.y >= hex.y + (tile_hex.x - hex.x)
|
||||
if tile_hex.x >= piece_hex.x {
|
||||
tile_hex.y >= piece_hex.y + (tile_hex.x - piece_hex.x)
|
||||
} 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) {
|
||||
plays.push(PlayInfo {
|
||||
valid: true,
|
||||
threat: false,
|
||||
play: Play::from_alt(piece.tile, tile_id),
|
||||
check: CheckState::new(),
|
||||
blocking: 0,
|
||||
@ -569,11 +633,7 @@ impl Game {
|
||||
|
||||
pub fn get_alts(&self, piece:&Piece) -> Vec<Play>
|
||||
{
|
||||
let mut plays = Vec::new();
|
||||
for info in self.get_alts_data(piece) {
|
||||
plays.push(info.play);
|
||||
}
|
||||
plays
|
||||
self.get_alts_data(piece).iter().map(|info| info.play).collect()
|
||||
}
|
||||
|
||||
pub fn get_drops(&self, piece:&Piece) -> Vec<Play>
|
||||
@ -601,12 +661,12 @@ impl Game {
|
||||
/*
|
||||
** Target must be same color.
|
||||
*/
|
||||
valid = valid && piece.player == target.player;
|
||||
valid &= piece.player == target.player;
|
||||
|
||||
/*
|
||||
** 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.
|
||||
@ -621,7 +681,7 @@ impl Game {
|
||||
** Target must have movement in reverse direction.
|
||||
*/
|
||||
let moves = target.moves().rotate().direction;
|
||||
valid = valid && (mask & moves) != 0;
|
||||
valid &= (mask & moves) != 0;
|
||||
}
|
||||
}
|
||||
valid
|
||||
@ -632,16 +692,18 @@ impl Game {
|
||||
let hex = Hex::from_tile(tile_id);
|
||||
let tile = &self.board.tiles[tile_id as usize];
|
||||
|
||||
let mut piece = piece.clone();
|
||||
piece.tile = tile_id;
|
||||
|
||||
/*
|
||||
** Tile must not be occupied.
|
||||
*/
|
||||
let mut valid = tile.piece.is_none();
|
||||
|
||||
/*
|
||||
** If in check, a piece may only be dropped if check
|
||||
** is due to a multi-tile move from a single piece.
|
||||
** If in check, a piece may only be dropped onto a tile blocking check.
|
||||
*/
|
||||
valid = valid && match self.status {
|
||||
valid &= match self.status {
|
||||
GameStatus::Check(state) => {
|
||||
!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.
|
||||
*/
|
||||
if (flags & flags::IGNORE_EXTENT as u32) == 0 {
|
||||
valid = valid && if piece.player == 0 {
|
||||
hex.y < self.board.columns[hex.x as usize].extent[1] as i8
|
||||
valid &= if piece.player == 0 {
|
||||
hex.y <= self.board.columns[hex.x as usize].extent[1] as i8
|
||||
} 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 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 (flags & flags::IGNORE_CHECK as u32) == 0 {
|
||||
for mv in piece_moves {
|
||||
if let Some(target_id) = self.board.tiles[mv.to as usize].piece {
|
||||
if let Some(target) = self.board.pieces[target_id as usize] {
|
||||
if target.class == PIECE_HEART && target.player != piece.player {
|
||||
if mv.valid && mv.check.is_check() {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
valid = false;
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ impl MoveSet {
|
||||
Self {
|
||||
direction:rotate6(self.direction),
|
||||
stride:rotate12(self.stride),
|
||||
alt:None,
|
||||
alt:self.alt,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -76,7 +76,7 @@ pub const PIECES :[PieceClass; PIECES_COUNT] = [
|
||||
PieceClass {
|
||||
name: "Lance",
|
||||
moves: MoveSet::new()
|
||||
.add(bit(1) | bit(5))
|
||||
.add(bit(1) | bit(3) | bit(5))
|
||||
.add_stride(bit(0), 3),
|
||||
pmoves: MoveSet::new()
|
||||
.add(bit(0) | bit(3))
|
||||
|
@ -9,14 +9,17 @@ pub const fn rotate6(x:u32) -> u32
|
||||
const R1 :u32 = 0x0000_003F;
|
||||
const R2 :u32 = 0x0000_0FC0;
|
||||
const R3 :u32 = 0x0003_F000;
|
||||
const R4 :u32 = 0x00FC_0000;
|
||||
|
||||
let a = (x & R1) << 3;
|
||||
let b = (x & R2) << 3;
|
||||
let c = (x & R3) << 3;
|
||||
let d = (x & R4) << 3;
|
||||
|
||||
(a & R1) | ((a >> 6) & R1)
|
||||
| (b & R2) | ((b >> 6) & R2)
|
||||
| (c & R3) | ((c >> 6) & R3)
|
||||
| (d & R4) | ((d >> 6) & R4)
|
||||
}
|
||||
|
||||
pub const fn rotate12(x:u32) -> u32
|
||||
@ -24,8 +27,8 @@ pub const fn rotate12(x:u32) -> u32
|
||||
const R1 :u32 = 0x0000_0FFF;
|
||||
const R2 :u32 = 0x00FF_F000;
|
||||
|
||||
let a = (x & R1) << 3;
|
||||
let b = (x & R2) << 3;
|
||||
let a = (x & R1) << 6;
|
||||
let b = (x & R2) << 6;
|
||||
|
||||
(a & R1) | ((a >> 12) & R1)
|
||||
| (b & R2) | ((b >> 12) & R2)
|
||||
|
@ -122,7 +122,7 @@ GAME.Tile = class {
|
||||
this.piece = null;
|
||||
|
||||
this.threaten = [0, 0];
|
||||
this.checking = 0;
|
||||
this.checking = false;
|
||||
|
||||
this.hex = HEX.tile_to_hex(index);
|
||||
}
|
||||
@ -136,7 +136,7 @@ GAME.Tile = class {
|
||||
|
||||
reset() {
|
||||
this.threaten = [0, 0];
|
||||
this.checking = 0;
|
||||
this.checking = false;
|
||||
}
|
||||
};
|
||||
|
||||
@ -294,7 +294,7 @@ GAME.Game = class {
|
||||
}
|
||||
if(movement.valid && movement.check != 0) {
|
||||
is_checking = true;
|
||||
this.board.tiles[movement.tile].checking |= movement.check;
|
||||
this.board.tiles[movement.tile].checking = true;
|
||||
this.state.check |= movement.check;
|
||||
}
|
||||
if(movement.block != 0) {
|
||||
@ -303,7 +303,7 @@ GAME.Game = class {
|
||||
}
|
||||
|
||||
if(is_checking) {
|
||||
this.board.tiles[piece.tile].checking = 1;
|
||||
this.board.tiles[piece.tile].checking = true;
|
||||
checking_pieces++;
|
||||
}
|
||||
}
|
||||
@ -522,7 +522,7 @@ GAME.Game = class {
|
||||
let check_count = this.state.check & 0x3F;
|
||||
if(piece.player == (this.turn & 1) && this.state.check != 0) {
|
||||
if(piece.piece != GAME.Const.PieceId.Heart) {
|
||||
if(tile_data.checking != 0) {
|
||||
if(tile_data.checking) {
|
||||
if(target_id !== null) {
|
||||
if(check_count > 1) {
|
||||
result = false;
|
||||
@ -776,7 +776,7 @@ GAME.Game = class {
|
||||
let check_count = this.state.check & 0x3F;
|
||||
if(piece.player == (this.turn & 1) && this.state.check != 0) {
|
||||
if(check_direct == 0 && check_count == 1) {
|
||||
position_valid = tile.checking != 0;
|
||||
position_valid = tile.checking;
|
||||
} else {
|
||||
position_valid = false;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user