Added rust solver to the repository

This commit is contained in:
Lukas Wölfer
2025-08-08 19:16:08 +02:00
parent a9ca38e812
commit 5fdf1602eb
91 changed files with 7047 additions and 0 deletions

View File

@@ -0,0 +1,405 @@
use board::{
Board, BunkerSlot, CardType, CardTypeNoHua, FieldPosition, NumberCard, PositionNoGoal,
SpecialCardType,
};
use serde::{Deserialize, Serialize};
pub(super) trait BoardApplication {
fn apply(&self, solboard: &mut Board);
fn undo(&self, solboard: &mut Board);
fn can_apply(&self, solboard: &Board) -> bool;
fn can_undo(&self, solboard: &Board) -> bool;
fn checked_apply(&self, solboard: &mut Board) -> bool {
if self.can_apply(solboard) {
self.apply(solboard);
return true;
}
return false;
}
fn checked_undo(&self, solboard: &mut Board) -> bool {
if self.can_undo(solboard) {
self.undo(solboard);
return true;
}
return false;
}
}
fn can_pop_top(solboard: &Board, position: &PositionNoGoal, card: &CardType) -> bool {
match position {
PositionNoGoal::Field(fieldpos) => {
if solboard.field[usize::from(fieldpos.column())]
.last()
.expect("Trying to pop top of empty field stack")
!= card
{
return false;
}
}
PositionNoGoal::Bunker { slot_index } => {
if solboard.bunker[usize::from(*slot_index)] != BunkerSlot::Stash(card.remove_hua()) {
return false;
}
}
};
return true;
}
fn pop_top(solboard: &mut Board, position: &PositionNoGoal, card: &CardType) {
debug_assert!(can_pop_top(solboard, position, card));
match position {
PositionNoGoal::Field(fieldpos) => {
solboard
.field
.get_mut(usize::from(fieldpos.column()))
.expect("Column index fucked")
.pop();
}
PositionNoGoal::Bunker { slot_index } => {
solboard.bunker[usize::from(*slot_index)] = BunkerSlot::Empty;
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct Goal {
pub card: NumberCard,
pub source: PositionNoGoal,
pub goal_slot_index: u8,
}
impl BoardApplication for Goal {
fn can_apply(&self, solboard: &Board) -> bool {
match &solboard.goal[usize::from(self.goal_slot_index)] {
Option::Some(NumberCard { value, suit }) => {
if self.card.value != *value + 1 {
return false;
}
if self.card.suit != *suit {
return false;
}
}
Option::None => {
if self.card.value != 1 {
return false;
}
}
}
if !can_pop_top(solboard, &self.source, &CardType::Number(self.card.clone())) {
return false;
}
return true;
}
fn can_undo(&self, _solboard: &Board) -> bool {
return true;
}
fn apply(&self, solboard: &mut Board) {
pop_top(solboard, &self.source, &CardType::Number(self.card.clone()));
*solboard
.goal
.get_mut(usize::from(self.goal_slot_index))
.expect("Slot index fucked") = Option::Some(self.card.clone());
}
fn undo(&self, solboard: &mut Board) {
match &self.source {
PositionNoGoal::Field(position) => {
solboard
.field
.get_mut(usize::from(position.column()))
.expect("Column index fucked")
.push(CardType::Number(self.card.clone()));
}
PositionNoGoal::Bunker { slot_index } => {
solboard.bunker[usize::from(*slot_index)] =
BunkerSlot::Stash(CardTypeNoHua::Number(self.card.clone()));
}
}
if self.card.value == 1 {
solboard.goal[usize::from(self.goal_slot_index)] = Option::None;
} else {
solboard.goal[usize::from(self.goal_slot_index)] = Option::Some(NumberCard {
suit: self.card.suit.clone(),
value: self.card.value - 1,
});
}
}
}
impl std::fmt::Display for Goal {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
return write!(
f,
"Goal {} from {} to slot #{}",
self.card, self.source, self.goal_slot_index
);
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct DragonKill {
pub card: SpecialCardType,
pub source: [PositionNoGoal; 4],
pub destination_slot_index: u8,
}
impl BoardApplication for DragonKill {
fn apply(&self, solboard: &mut Board) {
for position in &self.source {
pop_top(solboard, position, &CardType::Special(self.card.clone()));
}
solboard.bunker[usize::from(self.destination_slot_index)] =
BunkerSlot::Blocked(Option::Some(self.card.clone()));
}
fn undo(&self, solboard: &mut Board) {
solboard.bunker[usize::from(self.destination_slot_index)] = BunkerSlot::Empty;
for position in &self.source {
match &position {
PositionNoGoal::Field(field_position) => {
solboard.field[usize::from(field_position.column())]
.push(CardType::Special(self.card.clone()));
}
PositionNoGoal::Bunker { slot_index } => {
solboard.bunker[usize::from(*slot_index)] =
BunkerSlot::Stash(CardTypeNoHua::Special(self.card.clone()));
}
}
}
}
fn can_apply(&self, solboard: &Board) -> bool {
if self.destination_slot_index >= 3 {
return false;
}
let previous_slot_empty = solboard
.bunker
.iter()
.take(self.destination_slot_index.saturating_sub(1).into())
.all(|x| {
if let BunkerSlot::Empty = x {
return true;
} else {
return false;
}
});
if previous_slot_empty {
return false;
}
return true;
}
fn can_undo(&self, _solboard: &Board) -> bool {
return true;
}
}
impl std::fmt::Display for DragonKill {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
return write!(
f,
"Kill {} to bunker #{} from {}, {}, {}, {}",
self.card,
self.destination_slot_index,
self.source[0],
self.source[1],
self.source[2],
self.source[3],
);
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct Bunkerize {
pub card: CardTypeNoHua,
pub bunker_slot_index: u8,
pub field_position: FieldPosition,
pub to_bunker: bool,
}
impl Bunkerize {
fn can_move_to_bunker(&self, solboard: &Board) -> bool {
if self.field_position.row() + 1
!= solboard.field[usize::from(self.field_position.column())].len() as u8
{
return false;
}
if self.card.add_hua()
!= *solboard.field[usize::from(self.field_position.column())]
.last()
.unwrap()
{
return false;
}
if solboard.bunker[usize::from(self.bunker_slot_index)] != BunkerSlot::Empty {
return false;
}
return true;
}
fn move_to_bunker(&self, solboard: &mut Board) {
debug_assert!(self.can_move_to_bunker(solboard));
solboard.field[usize::from(self.field_position.column())].pop();
solboard.bunker[usize::from(self.bunker_slot_index)] = BunkerSlot::Stash(self.card.clone());
}
fn can_move_from_bunker(&self, solboard: &Board) -> bool {
if solboard.bunker[usize::from(self.bunker_slot_index)]
!= BunkerSlot::Stash(self.card.clone())
{
return false;
}
if self.field_position.row()
!= solboard.field[usize::from(self.field_position.column())].len() as u8
{
return false;
}
return true;
}
fn move_from_bunker(&self, solboard: &mut Board) {
debug_assert!(self.can_move_from_bunker(solboard));
solboard.field[usize::from(self.field_position.column())].push(self.card.add_hua());
solboard.bunker[usize::from(self.bunker_slot_index)] = BunkerSlot::Empty;
}
}
impl BoardApplication for Bunkerize {
fn apply(&self, solboard: &mut Board) {
if self.to_bunker {
self.move_to_bunker(solboard);
} else {
self.move_from_bunker(solboard);
}
}
fn undo(&self, solboard: &mut Board) {
if self.to_bunker {
self.move_from_bunker(solboard);
} else {
self.move_to_bunker(solboard);
}
}
fn can_apply(&self, solboard: &Board) -> bool {
if self.to_bunker {
return self.can_move_to_bunker(solboard);
} else {
return self.can_move_from_bunker(solboard);
}
}
fn can_undo(&self, solboard: &Board) -> bool {
if self.to_bunker {
return self.can_move_from_bunker(solboard);
} else {
return self.can_move_to_bunker(solboard);
}
}
}
impl std::fmt::Display for Bunkerize {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.to_bunker {
return write!(
f,
"Move {} from {} to bunker #{}",
self.card, self.field_position, self.bunker_slot_index,
);
} else {
return write!(
f,
"Move {} from bunker #{} to {}",
self.card, self.bunker_slot_index, self.field_position,
);
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct HuaKill {
pub field_position: FieldPosition,
}
impl BoardApplication for HuaKill {
fn can_apply(&self, solboard: &Board) -> bool {
if solboard.field[usize::from(self.field_position.column())].last()
!= Option::Some(&CardType::Hua)
{
return false;
}
if solboard.field[usize::from(self.field_position.column())].len()
!= (self.field_position.row() + 1) as usize
{
return false;
}
return true;
}
fn apply(&self, solboard: &mut Board) {
debug_assert!(self.can_apply(solboard));
solboard.field[usize::from(self.field_position.column())].pop();
solboard.hua_set = true;
}
fn can_undo(&self, solboard: &Board) -> bool {
if solboard.field[usize::from(self.field_position.column())].len()
!= self.field_position.row() as usize
{
return false;
}
return true;
}
fn undo(&self, solboard: &mut Board) {
debug_assert!(self.can_undo(solboard));
solboard.field[usize::from(self.field_position.column())].push(CardType::Hua);
solboard.hua_set = false;
}
}
impl std::fmt::Display for HuaKill {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
return write!(f, "Kill hua from {}", self.field_position);
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub enum All {
Bunkerize(Bunkerize),
DragonKill(DragonKill),
Goal(Goal),
HuaKill(HuaKill),
Move(super::Move),
}
impl std::fmt::Display for All {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Bunkerize(x) => return write!(f, "{}", x),
Self::DragonKill(x) => return write!(f, "{}", x),
Self::Goal(x) => return write!(f, "{}", x),
Self::HuaKill(x) => return write!(f, "{}", x),
Self::Move(x) => return write!(f, "{}", x),
}
}
}
impl All {
pub fn apply(&self, solboard: &mut Board) {
match self {
Self::HuaKill(obj) => {
obj.apply(solboard);
}
Self::DragonKill(obj) => {
obj.apply(solboard);
}
Self::Goal(obj) => {
obj.apply(solboard);
}
Self::Bunkerize(obj) => {
obj.apply(solboard);
}
Self::Move(obj) => obj.apply(solboard),
}
}
pub fn undo(&self, solboard: &mut Board) {
match self {
Self::HuaKill(obj) => {
obj.undo(solboard);
}
Self::DragonKill(obj) => {
obj.undo(solboard);
}
Self::Goal(obj) => {
obj.undo(solboard);
}
Self::Bunkerize(obj) => {
obj.undo(solboard);
}
Self::Move(obj) => obj.undo(solboard),
}
}
}