Skip to content

Commit

Permalink
feat!: --dora flag now accepts tiles instead of number
Browse files Browse the repository at this point in the history
  • Loading branch information
DrCheeseFace committed Sep 6, 2024
1 parent b228167 commit f19a849
Show file tree
Hide file tree
Showing 7 changed files with 284 additions and 42 deletions.
45 changes: 21 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,22 @@ note: the winning group has to go last (this is to calculate fu correctly)
### Using file input
```
# hands.txt
--tiles EEw NNw SSw WWw rrd wwd ggd -w gd -p Ew -s Ew -d 2
--tiles 123p 456p 789p rrrdo 99p -w 9p -p Ew -s Ew -d 2'
--tiles 1p 9p 1s 9s 1m 9m rd gd wd Ew Sw Nw WWw -w Ww -p Ew -s Ew
--tiles 11z NNw SSw WWw rrd wwd ggd -w gd -p Ew -s Ew -d Ew Ew
-m 4 30 --ba 3
```


```bash
~/$ mahc -f hands.txt
> Dealer: 144000 (48000)

❯ Dealer: 96000 (32000)
Non-dealer: 64000 (16000/32000)
Yaku:
KokushiMusou Yakuman
KokushiMusou Yakuman 13 sided wait

Dealer: 144000 (48000)
Non-dealer: 96000 (24000/48000)
Yaku:
Tsuuiisou Yakuman
Expand All @@ -60,39 +67,29 @@ note: the winning group has to go last (this is to calculate fu correctly)
4 Han/ 30 Fu/ 3 Honba
Dealer: 12500 (4200)
non-dealer: 8600 (2300/4200)

6 Han/ 30 Fu
Dealer: 18000 (6000)
Non-dealer: 12000 (3000/6000)
Dora: 2
Yaku:
Honitsu: 2
Ittsuu: 1
Yakuhai: 1
Fu:
BasePoints: 20
NonSimpleOpenTriplet: 4
SingleWait: 2
```
### Json out
in ***normal mode***
```bash
~/$ mahc --tiles 123p 456p 789p rrrdo 99p -w 9p -p Ew -s Ew -d 2 --json
~/$ mahc --tiles 123p 456p 789p rrrdo 99p -w 9p -p Ew -s Ew -d 9p --json
```
yields
```json
{
"dora":2,
"dora":1,
"fu":30,
"fuString":["BasePoints: 20","NonSimpleOpenTriplet: 4","SingleWait: 2"],
"han":6,
"fuString":[ "BasePoints: 20", "NonSimpleOpenTriplet: 4", "SingleWait: 2"
],
"han":5,
"honba":0,
"scores":{
"dealer":{"ron":18000,"tsumo":6000},
"non-dealer":{"ron":12000,"tsumo":{"dealer":6000,"non-dealer":3000}}
"dealer":{"ron":12000,"tsumo":4000},
"non-dealer":{
"ron":8000,
"tsumo":{"dealer":4000,"non-dealer":2000}
}
},
"yakuString":["Honitsu: 2","Ittsuu: 1","Yakuhai: 1"]
}
"yakuString":["Honitsu: 2","Ittsuu: 1","Yakuhai: 1"]}
```
and in ***calculator mode***
```bash
Expand Down
Binary file modified demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 23 additions & 3 deletions src/calc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::hand::Hand;
use crate::limit_hand::LimitHands;
use crate::payment::Payment;
use crate::score::{FuValue, HanValue, HonbaCounter, Score};
use crate::tile_group::TileGroup;
use crate::yaku::Yaku;

#[derive(Debug, PartialEq)]
Expand All @@ -27,7 +28,7 @@ impl std::fmt::Display for CalculatorErrors {
pub fn get_hand_score(
tiles: Vec<String>,
win: String,
dora: u32,
dora: Option<Vec<String>>,
seat: String,
prev: String,
tsumo: bool,
Expand Down Expand Up @@ -76,7 +77,17 @@ pub fn get_hand_score(
hand.calculate_fu(tsumo)
}
};
let han = yaku.0 + dora;

// get han from dora tiles
let doras: Option<Vec<TileGroup>> = dora.map(|dora_tiles| {
dora_tiles
.into_iter()
.filter_map(|tile| tile.try_into().ok())
.collect()
});
let dora_count = hand.get_dora_count(doras);

let han = yaku.0 + dora_count;
let fu_value = calculate_total_fu_value(&fu);

let mut has_yakuman = false;
Expand All @@ -92,7 +103,16 @@ pub fn get_hand_score(
//can unwrap here because check for yaku earlier
calculate(han, fu_value).unwrap()
};
let score = Score::new(payment, yaku.1, fu, han, fu_value, honba, hand.is_open());
let score = Score::new(
payment,
yaku.1,
fu,
han,
fu_value,
honba,
hand.is_open(),
dora_count,
);

Ok(score)
}
Expand Down
116 changes: 115 additions & 1 deletion src/hand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,77 @@ impl Hand {
fu_types
}

/// Get the dora count in the hand from dora indicator tiles.
///
/// # Examples
///
/// ```rust
/// use mahc::hand::Hand;
/// use mahc::tile_group::TileGroup;
/// let hand = Hand::new(
/// vec![
/// "123p".to_string(),
/// "505s".to_string(),
/// "EEEw".to_string(),
/// "9999m".to_string(),
/// "rrd".to_string(),
/// ],
/// "rd".to_string(),
/// "Ew".to_string(),
/// "Ew".to_string(),
/// )
/// .unwrap();
/// let doras: Vec<TileGroup> = vec![
/// "1p".to_string().try_into().unwrap(),
/// "4s".to_string().try_into().unwrap(),
/// "Nw".to_string().try_into().unwrap(),
/// "8m".to_string().try_into().unwrap(),
/// "gd".to_string().try_into().unwrap(),
/// ];
///
/// let dora = hand.get_dora_count(Some(doras));
/// assert_eq!(dora, 14);
/// ```
pub fn get_dora_count(&self, dora_indicator_tiles: Option<Vec<TileGroup>>) -> u32 {
let mut count = 0;
for group in &self.groups {
if group.isaka {
count += 1;
}
}
if dora_indicator_tiles.is_none() {
return count;
}
for tile in dora_indicator_tiles.unwrap() {
let dora_tile = tile.next_tile().unwrap();
for triplet in self.triplets() {
if triplet.value == dora_tile.value && triplet.suit == dora_tile.suit {
count += 3;
}
}
for kan in self.kans() {
if kan.value == dora_tile.value && kan.suit == dora_tile.suit {
count += 4;
}
}
for pair in self.pairs() {
if pair.value == dora_tile.value && pair.suit == dora_tile.suit {
count += 2;
}
}
for sequence in self.sequences() {
if (sequence.value == dora_tile.value
|| (sequence.parse_u8().unwrap() + 1).to_string() == dora_tile.value
|| (sequence.parse_u8().unwrap() + 2).to_string() == dora_tile.value)
&& sequence.suit == dora_tile.suit
{
count += 1;
}
}
}
count
}

/// Get the sequence groups in the hand.
pub fn sequences(&self) -> Vec<TileGroup> {
// TODO: We can do better than cloning into `into_iter()`.
Expand Down Expand Up @@ -2443,7 +2514,7 @@ mod tests {
mod tile_group_tests {
use super::Hand;
use crate::suit::Suit;
use crate::tile_group::GroupType;
use crate::tile_group::{GroupType, TileGroup};

#[test]
fn identify_pair() {
Expand Down Expand Up @@ -2594,4 +2665,47 @@ mod tile_group_tests {
assert_eq!(out.sequences()[0].suit, Suit::Pinzu);
assert!(out.sequences()[0].isopen);
}
#[test]
fn dora_count_all() {
let out = Hand::new(
vec![
"123p".to_string(),
"505s".to_string(),
"EEEw".to_string(),
"9999m".to_string(),
"rrd".to_string(),
],
"rd".to_string(),
"Ew".to_string(),
"Ew".to_string(),
)
.unwrap();
let dora_1: TileGroup = "1p".to_string().try_into().unwrap();
let dora_2: TileGroup = "4s".to_string().try_into().unwrap();
let dora_3: TileGroup = "Nw".to_string().try_into().unwrap();
let dora_4: TileGroup = "8m".to_string().try_into().unwrap();
let dora_5: TileGroup = "gd".to_string().try_into().unwrap();
let doras = vec![dora_1, dora_2, dora_3, dora_4, dora_5];

let dora = out.get_dora_count(Some(doras));
assert_eq!(dora, 14);
}
#[test]
fn dora_count_aka() {
let out = Hand::new(
vec![
"123p".to_string(),
"505s".to_string(),
"EEEw".to_string(),
"9999m".to_string(),
"rrd".to_string(),
],
"rd".to_string(),
"Ew".to_string(),
"Ew".to_string(),
)
.unwrap();
let dora = out.get_dora_count(None);
assert_eq!(dora, 1);
}
}
28 changes: 14 additions & 14 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ pub struct Args {
#[arg(short, long)]
win: Option<String>,

/// Han from dora
#[arg(short, long, default_value_t = 0)]
dora: u32,
/// Dora indicator tiles
#[arg(short, long, value_delimiter = ' ', num_args = 1..)]
dora: Option<Vec<String>>,

/// seat wind
#[arg(short, long, default_value = "Ew")]
Expand Down Expand Up @@ -125,7 +125,7 @@ pub fn parse_hand(args: &Args) -> Result<String, HandErr> {
let score = calc::get_hand_score(
args.tiles.clone().unwrap(),
args.win.clone().unwrap(),
args.dora,
args.dora.clone(),
args.seat.clone(),
args.prev.clone(),
args.tsumo,
Expand All @@ -142,9 +142,9 @@ pub fn parse_hand(args: &Args) -> Result<String, HandErr> {
//TODO VALIDATION (i dont care enough yet)

let printout = if args.json {
json_hand_out(&score, args)
json_hand_out(&score)
} else {
default_hand_out(&score, args)
default_hand_out(&score)
};
Ok(printout)
}
Expand Down Expand Up @@ -200,12 +200,12 @@ pub fn default_calc_out(
)
}

pub fn json_hand_out(score: &Score, args: &Args) -> String {
pub fn json_hand_out(score: &Score) -> String {
let out = json!({
"han" : score.han(),
"fu" : score.fu_score(),
"honba" : args.ba,
"dora" : args.dora,
"honba" : score.honba(),
"dora" : score.dora_count(),
"fuString" : score.fu().iter().map(|x| x.to_string()).collect::<Vec<String>>(),
"yakuString" : score.yaku().iter().map(|x| x.to_string(score.is_open())).collect::<Vec<String>>(),
"scores" : {
Expand All @@ -224,16 +224,16 @@ pub fn json_hand_out(score: &Score, args: &Args) -> String {
});
out.to_string()
}
pub fn default_hand_out(score: &Score, args: &Args) -> String {
pub fn default_hand_out(score: &Score) -> String {
let mut out: String = String::new();
if !score.yaku()[0].is_yakuman() {
if args.ba != 0 {
if score.honba() != 0 {
out.push_str(
format!(
"\n{} Han/ {} Fu/ {} Honba",
score.han(),
score.fu_score(),
args.ba
score.honba()
)
.as_str(),
)
Expand All @@ -256,8 +256,8 @@ pub fn default_hand_out(score: &Score, args: &Args) -> String {
.as_str(),
);

if !score.yaku()[0].is_yakuman() && args.dora != 0 {
out.push_str(format!("\nDora: {}", args.dora).as_str());
if !score.yaku()[0].is_yakuman() && score.dora_count() != 0 {
out.push_str(format!("\nDora: {}", score.dora_count()).as_str());
}

out.push_str("\nYaku: ");
Expand Down
9 changes: 9 additions & 0 deletions src/score.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub struct Score {
honba: HonbaCounter,
/// Is the hand open when it scored?
is_open: bool,
/// total number of han from dora
dora_count: u32,
}

impl Score {
Expand All @@ -38,6 +40,7 @@ impl Score {
fu_score: FuValue,
honba: HonbaCounter,
is_open: bool,
dora_count: u32,
) -> Self {
Self {
payment,
Expand All @@ -47,6 +50,7 @@ impl Score {
fu_score,
honba,
is_open,
dora_count,
}
}

Expand Down Expand Up @@ -84,4 +88,9 @@ impl Score {
pub fn is_open(&self) -> bool {
self.is_open
}

/// Get the total number of han from dora.
pub fn dora_count(&self) -> u32{
self.dora_count
}
}
Loading

0 comments on commit f19a849

Please sign in to comment.