Skip to content

Commit b0901a7

Browse files
committed
London Mulligan
1 parent 92ae3ef commit b0901a7

File tree

6 files changed

+126
-39
lines changed

6 files changed

+126
-39
lines changed

src/Game.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@ import OpenCards from './Cards.js';
44
import enums from './enum.json' with { type: 'json' };
55
import * as wasm from './rs/pkg/etg.js';
66

7-
const GameMoveType = ['end', 'cast', 'accept', 'mulligan', 'foe', 'resign'];
7+
const GameMoveType = [
8+
'end',
9+
'cast',
10+
'accept',
11+
'mulligan',
12+
'shuffle',
13+
'foe',
14+
'resign',
15+
];
816

917
export default class Game {
1018
constructor(data) {

src/rs/src/aieval.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,11 +1013,7 @@ fn evalthing(
10131013
.filter(|&r| r != 0)
10141014
.map(|r| eval_skill(ctx, r, ctx.getSkill(r, Event::Death), ttatk, damage, quantamap))
10151015
.sum::<i32>();
1016-
if j == 0 {
1017-
score += val
1018-
} else {
1019-
score -= val
1020-
}
1016+
if j == 0 { score += val } else { score -= val }
10211017
}
10221018
}
10231019
}
@@ -1091,7 +1087,16 @@ fn evalthing(
10911087
} else {
10921088
score = if ctx.material(id, None) { score * 5 / 4 } else { score * 7 / 2 };
10931089
}
1094-
score * if inhand { 2 } else if ctx.hasskill(id, Event::Turnstart, Skill::beguilestop) && !ctx.hasskill(id, Event::OwnAttack, Skill::singularity) { -1 } else { 3 }
1090+
score
1091+
* if inhand {
1092+
2
1093+
} else if ctx.hasskill(id, Event::Turnstart, Skill::beguilestop)
1094+
&& !ctx.hasskill(id, Event::OwnAttack, Skill::singularity)
1095+
{
1096+
-1
1097+
} else {
1098+
3
1099+
}
10951100
}
10961101

10971102
pub fn eval(ctx: &Game) -> i32 {

src/rs/src/game.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,10 @@ pub struct PlayerData {
8383
pub markpower: i8,
8484
pub deckpower: u8,
8585
pub drawpower: u8,
86+
pub tax: u8,
87+
pub quanta: [u8; 12],
8688
pub creatures: [i16; 23],
8789
pub permanents: [i16; 16],
88-
pub quanta: [u8; 12],
8990
pub hand: [i16; 8],
9091
pub deck: Rc<Vec<i16>>,
9192
}
@@ -157,6 +158,7 @@ pub enum GameMove {
157158
Cast(i16, i16),
158159
Accept,
159160
Mulligan,
161+
Shuffle(i16),
160162
Foe(i16),
161163
Resign(i16),
162164
}
@@ -168,8 +170,9 @@ impl From<GameMove> for [i16; 3] {
168170
GameMove::Cast(c, t) => [1, c, t],
169171
GameMove::Accept => [2, 0, 0],
170172
GameMove::Mulligan => [3, 0, 0],
171-
GameMove::Foe(t) => [4, 0, t],
172-
GameMove::Resign(c) => [5, c, 0],
173+
GameMove::Shuffle(t) => [4, 0, t],
174+
GameMove::Foe(t) => [5, 0, t],
175+
GameMove::Resign(c) => [6, c, 0],
173176
}
174177
}
175178
}
@@ -181,8 +184,9 @@ impl From<[i16; 3]> for GameMove {
181184
1 => GameMove::Cast(cmd[1], cmd[2]),
182185
2 => GameMove::Accept,
183186
3 => GameMove::Mulligan,
184-
4 => GameMove::Foe(cmd[2]),
185-
5 => GameMove::Resign(cmd[1]),
187+
4 => GameMove::Shuffle(cmd[2]),
188+
5 => GameMove::Foe(cmd[2]),
189+
6 => GameMove::Resign(cmd[1]),
186190
_ => GameMove::Mulligan,
187191
}
188192
}
@@ -679,6 +683,15 @@ impl Game {
679683
self.get_player(id).quanta(ele)
680684
}
681685

686+
pub fn tax_left(&self, id: i16) -> u8 {
687+
let player = self.get_player(id);
688+
player.tax - (7 - player.hand_len() as u8)
689+
}
690+
691+
pub fn tax(&self, id: i16) -> u8 {
692+
self.get_player(id).tax
693+
}
694+
682695
pub fn count_creatures(&self, id: i16) -> i16 {
683696
self.get_player(id).creatures.into_iter().map(|cr| (cr != 0) as i16).sum()
684697
}
@@ -2729,7 +2742,21 @@ impl Game {
27292742
}
27302743
if handlen != usize::MAX {
27312744
self.fx(self.turn, Fx::Sfx(Sfx::mulligan));
2732-
self.drawhand(self.turn, handlen);
2745+
self.drawhand(self.turn, 7);
2746+
let player = self.get_player_mut(self.turn);
2747+
if player.tax < 7 {
2748+
player.tax += 1;
2749+
}
2750+
}
2751+
}
2752+
GameMove::Shuffle(t) => {
2753+
self.remove(t);
2754+
let decklen = self.get_player(self.turn).deck.len() as u32;
2755+
let idx = self.upto(decklen + 1) as usize;
2756+
let mut player = self.get_player_mut(self.turn);
2757+
player.deck_mut().insert(idx, t);
2758+
if player.hand_len() + player.tax as usize == 7 {
2759+
self.r#move(GameMove::Accept);
27332760
}
27342761
}
27352762
GameMove::Foe(t) => {

src/rs/src/skill.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2844,7 +2844,7 @@ impl Skill {
28442844
ctx.fx(t, Fx::Forced);
28452845
ctx.turn = realturn;
28462846
ctx.useactive(t, tgt);
2847-
return
2847+
return;
28482848
}
28492849
}
28502850
ctx.turn = realturn;
@@ -2873,7 +2873,8 @@ impl Skill {
28732873
let mut thing = ctx.get_thing_mut(t);
28742874
if thing.kind == Kind::Creature {
28752875
thing.status.insert(Stat::hp, 1);
2876-
if let Some(amt) = thing.status.get_mut(Stat::maxhp).map(|hp| core::mem::replace(hp, 1)) {
2876+
if let Some(amt) = thing.status.get_mut(Stat::maxhp).map(|hp| core::mem::replace(hp, 1))
2877+
{
28772878
if amt > 0 {
28782879
let maxhp = ctx.get_mut(ctx.get_owner(c), Stat::maxhp);
28792880
*maxhp = (*maxhp + amt - 1).min(500);
@@ -2887,7 +2888,8 @@ impl Skill {
28872888
let mut thing = ctx.get_thing_mut(t);
28882889
if thing.kind == Kind::Creature {
28892890
thing.status.insert(Stat::hp, 1);
2890-
if let Some(amt) = thing.status.get_mut(Stat::maxhp).map(|hp| core::mem::replace(hp, 1)) {
2891+
if let Some(amt) = thing.status.get_mut(Stat::maxhp).map(|hp| core::mem::replace(hp, 1))
2892+
{
28912893
if amt > 0 {
28922894
let maxhp = ctx.get_mut(ctx.get_owner(c), Stat::maxhp);
28932895
*maxhp = (*maxhp + amt - 1).min(500);
@@ -3548,9 +3550,7 @@ impl Skill {
35483550
ctx.set(t, Flag::airborne, true);
35493551
}
35503552
}
3551-
Self::kindle => {
3552-
ctx.incrStatus(c, Stat::dive, 1)
3553-
}
3553+
Self::kindle => ctx.incrStatus(c, Stat::dive, 1),
35543554
Self::lightning => {
35553555
ctx.fx(t, Fx::Lightning);
35563556
ctx.spelldmg(t, 5);
@@ -4388,7 +4388,7 @@ impl Skill {
43884388
let owner = ctx.get_owner(c);
43894389
if ctx.phase == Phase::Mulligan && t == owner {
43904390
ctx.set(ctx.get_foe(owner), Flag::sabbath, true);
4391-
return
4391+
return;
43924392
}
43934393
if !ctx.sanctified(t) {
43944394
ctx.set(t, Flag::sabbath, true);

src/rs/src/text.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,8 +491,16 @@ impl<'a> SkillThing<'a> {
491491
} else {
492492
"Transform this card into a Fungus"
493493
}),
494-
Skill::gaincharge(x) if ev == Event::Death => Cow::from(format!("Whenever any creature dies, gain {} stack{}", x, if x == 1 { "" } else { "s" })),
495-
Skill::gaincharge(x) if ev == Event::Destroy => Cow::from(format!("Whenever any other permanent is destroyed, gain {} stack{}", x, if x == 1 { "" } else { "s" })),
494+
Skill::gaincharge(x) if ev == Event::Death => Cow::from(format!(
495+
"Whenever any creature dies, gain {} stack{}",
496+
x,
497+
if x == 1 { "" } else { "s" }
498+
)),
499+
Skill::gaincharge(x) if ev == Event::Destroy => Cow::from(format!(
500+
"Whenever any other permanent is destroyed, gain {} stack{}",
501+
x,
502+
if x == 1 { "" } else { "s" }
503+
)),
496504
Skill::gaintimecharge => Cow::from(
497505
"Gain one stack for every card you draw. Does not gain a stack from your draw at the start of your turn",
498506
),

src/views/Match.jsx

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,7 +1141,9 @@ export default function Match(props) {
11411141
const endClick = (discard = 0) => {
11421142
const game = pgame();
11431143
if (game.turn === p1id() && game.phase === Phase.Mulligan) {
1144-
applyNext({ x: 'accept' });
1144+
if (game.tax_left(p1id()) === 0) {
1145+
applyNext({ x: 'accept' });
1146+
}
11451147
} else if (game.winner) {
11461148
gotoResult();
11471149
} else if (game.turn === p1id()) {
@@ -1163,13 +1165,44 @@ export default function Match(props) {
11631165
}
11641166
};
11651167

1168+
const shuffleClick = t => {
1169+
applyNext({ x: 'shuffle', t });
1170+
const game = pgame(),
1171+
tax_left = game.tax_left(p1id());
1172+
setTargeting(
1173+
tax_left === 0 ? null : (
1174+
{
1175+
filter: id =>
1176+
game.get_kind(id) === Kind.Spell && game.get_owner(id) === p1id(),
1177+
cb: shuffleClick,
1178+
text: 'Shuffle ' + tax_left,
1179+
src: null,
1180+
}
1181+
),
1182+
);
1183+
};
1184+
11661185
const cancelClick = () => {
1167-
const game = pgame();
1186+
let game = pgame();
11681187
if (resigning()) {
11691188
setResigning(false);
11701189
} else if (game.turn === p1id()) {
11711190
if (game.phase === Phase.Mulligan && !game.empty_hand(p1id())) {
11721191
applyNext({ x: 'mulligan' });
1192+
game = pgame();
1193+
const tax_left = game.tax_left(p1id());
1194+
setTargeting(
1195+
tax_left === 0 ? null : (
1196+
{
1197+
filter: id =>
1198+
game.get_kind(id) === Kind.Spell &&
1199+
game.get_owner(id) === p1id(),
1200+
cb: shuffleClick,
1201+
text: 'Shuffle ' + tax_left,
1202+
src: null,
1203+
}
1204+
),
1205+
);
11731206
} else {
11741207
setTargeting(null);
11751208
}
@@ -1191,14 +1224,18 @@ export default function Match(props) {
11911224
const thingClick = id => {
11921225
const game = pgame();
11931226
clearCard();
1194-
if (props.replay || game.phase !== Phase.Play) return;
1227+
if (props.replay || game.phase === Phase.End) return;
11951228
const tgting = targeting();
11961229
if (tgting) {
11971230
if (tgting.filter(id)) {
11981231
setTargeting(null);
11991232
tgting.cb(id);
12001233
}
1201-
} else if (game.get_owner(id) === p1id() && game.canactive(id)) {
1234+
} else if (
1235+
game.phase === Phase.Play &&
1236+
game.get_owner(id) === p1id() &&
1237+
game.canactive(id)
1238+
) {
12021239
const cb = tgt => applyNext({ x: 'cast', c: id, t: tgt });
12031240
if (
12041241
(game.get_kind(id) === Kind.Spell &&
@@ -1400,23 +1437,25 @@ export default function Match(props) {
14001437
let turntell, endText, cancelText;
14011438
if (g.phase !== Phase.End) {
14021439
turntell =
1403-
targeting() ?
1404-
targeting().text
1405-
: `${g.turn === p1 ? 'Your' : 'Their'} turn${
1406-
g.phase > Phase.Mulligan ? ''
1407-
: p1 === 1 ? "\nYou're first"
1408-
: "\nYou're second"
1409-
}`;
1440+
targeting()?.text ??
1441+
`${g.turn === p1 ? 'Your' : 'Their'} turn${
1442+
g.phase > Phase.Mulligan ? ''
1443+
: p1 === 1 ? "\nYou're first"
1444+
: "\nYou're second"
1445+
}`;
14101446
if (g.turn === p1) {
1411-
endText =
1412-
targeting() ? ''
1413-
: g.phase === Phase.Play ? 'End Turn'
1414-
: g.turn === p1 ? 'Accept'
1415-
: '';
14161447
if (g.phase !== Phase.Play) {
1417-
cancelText = g.turn === p1 ? 'Mulligan' : '';
1448+
cancelText = 'Mulligan';
1449+
const tax_left = g.tax_left(p1);
1450+
if (tax_left === 0) {
1451+
endText = 'Accept';
1452+
} else {
1453+
endText = '';
1454+
turntell += `\nYou're ${p1 === 1 ? 'first' : 'second'}`;
1455+
}
14181456
} else {
14191457
cancelText = targeting() || resigning() ? 'Cancel' : '';
1458+
endText = targeting() ? '' : 'End Turn';
14201459
}
14211460
} else cancelText = endText = '';
14221461
} else {

0 commit comments

Comments
 (0)