use crate::types::{Hand, TILE_MAX};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Mentsu {
Koutsu(u8),
Shuntsu(u8),
}
#[derive(Debug, Clone)]
pub struct Division {
pub head: u8,
pub body: Vec<Mentsu>,
}
pub fn is_tenpai(hand: &mut Hand) -> bool {
for i in 0..crate::types::TILE_MAX {
if hand.counts[i] < 4 {
hand.add(i as u8);
if is_kokushi(hand) {
hand.remove(i as u8);
return true;
}
if is_chiitoitsu(hand) {
hand.remove(i as u8);
return true;
}
let c = hand.counts[i];
let check_standard = if i >= 27 {
c >= 2
} else {
let has_p = if i % 9 > 0 {
hand.counts[i - 1] > 0
} else {
false
};
let has_n = if i % 9 < 8 {
hand.counts[i + 1] > 0
} else {
false
};
c >= 2 || has_p || has_n
};
if check_standard && is_standard_agari(hand) {
hand.remove(i as u8);
return true;
}
hand.remove(i as u8); }
}
false
}
pub fn is_agari(hand: &mut Hand) -> bool {
if is_kokushi(hand) {
return true;
}
if is_chiitoitsu(hand) {
return true;
}
is_standard_agari(hand)
}
pub fn find_divisions(hand: &Hand) -> Vec<Division> {
let mut divisions = Vec::new();
for i in 0..TILE_MAX {
if hand.counts[i] >= 2 {
let mut processing_hand = hand.clone();
processing_hand.counts[i] -= 2;
let mut bodies = Vec::new();
let mut current_body = Vec::new();
decompose_all(&mut processing_hand, 0, &mut current_body, &mut bodies);
for body in bodies {
divisions.push(Division {
head: i as u8,
body,
});
}
}
}
divisions
}
fn decompose_all(
hand: &mut Hand,
start_idx: usize,
current_body: &mut Vec<Mentsu>,
results: &mut Vec<Vec<Mentsu>>,
) {
let mut i = start_idx;
while i < TILE_MAX && hand.counts[i] == 0 {
i += 1;
}
if i == TILE_MAX {
results.push(current_body.clone());
return;
}
if hand.counts[i] >= 3 {
hand.counts[i] -= 3;
current_body.push(Mentsu::Koutsu(i as u8));
decompose_all(hand, i, current_body, results);
current_body.pop();
hand.counts[i] += 3;
}
if i < 27 {
let is_valid_seq_start = match i {
0..=6 => true, 9..=15 => true, 18..=24 => true, _ => false,
};
if is_valid_seq_start && hand.counts[i + 1] > 0 && hand.counts[i + 2] > 0 {
hand.counts[i] -= 1;
hand.counts[i + 1] -= 1;
hand.counts[i + 2] -= 1;
current_body.push(Mentsu::Shuntsu(i as u8));
decompose_all(hand, i, current_body, results);
current_body.pop();
hand.counts[i] += 1;
hand.counts[i + 1] += 1;
hand.counts[i + 2] += 1;
}
}
}
pub fn is_kokushi(hand: &Hand) -> bool {
let kokushi_indices = [0, 8, 9, 17, 18, 26, 27, 28, 29, 30, 31, 32, 33];
let mut pair_found = false;
for &idx in &kokushi_indices {
let c = hand.counts[idx];
if c == 0 {
return false;
}
if c == 2 {
if pair_found {
return false; }
pair_found = true;
} else if c > 2 {
return false;
}
}
pair_found
}
pub fn is_chiitoitsu(hand: &Hand) -> bool {
let mut pairs = 0;
for c in hand.counts.iter() {
if *c == 2 {
pairs += 1;
} else if *c != 0 {
return false;
}
}
pairs == 7
}
pub fn is_standard_agari(hand: &mut Hand) -> bool {
for i in 0..TILE_MAX {
if hand.counts[i] >= 2 {
hand.counts[i] -= 2;
if decompose(hand, 0) {
hand.counts[i] += 2; return true;
}
hand.counts[i] += 2; }
}
false
}
fn decompose(hand: &mut Hand, start_idx: usize) -> bool {
let mut i = start_idx;
while i < TILE_MAX && hand.counts[i] == 0 {
i += 1;
}
if i == TILE_MAX {
return true; }
if hand.counts[i] >= 3 {
hand.counts[i] -= 3;
if decompose(hand, i) {
hand.counts[i] += 3; return true;
}
hand.counts[i] += 3; }
if i < 27 {
if let Some(is_valid_seq_start) = match i {
0..=6 => Some(true), 9..=15 => Some(true), 18..=24 => Some(true), _ => None,
} {
if is_valid_seq_start && hand.counts[i + 1] > 0 && hand.counts[i + 2] > 0 {
hand.counts[i] -= 1;
hand.counts[i + 1] -= 1;
hand.counts[i + 2] -= 1;
if decompose(hand, i) {
hand.counts[i] += 1;
hand.counts[i + 1] += 1;
hand.counts[i + 2] += 1;
return true;
}
hand.counts[i] += 1;
hand.counts[i + 1] += 1;
hand.counts[i + 2] += 1;
}
}
}
false
}