commit
5290b281bb
5 changed files with 1699 additions and 0 deletions
@ -0,0 +1,120 @@ |
|||
# This file is automatically @generated by Cargo. |
|||
# It is not intended for manual editing. |
|||
[[package]] |
|||
name = "bitvec" |
|||
version = "0.19.5" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" |
|||
dependencies = [ |
|||
"funty", |
|||
"radium", |
|||
"tap", |
|||
"wyz", |
|||
] |
|||
|
|||
[[package]] |
|||
name = "cfg-if" |
|||
version = "1.0.0" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" |
|||
|
|||
[[package]] |
|||
name = "funty" |
|||
version = "1.1.0" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" |
|||
|
|||
[[package]] |
|||
name = "getrandom" |
|||
version = "0.2.3" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" |
|||
dependencies = [ |
|||
"cfg-if", |
|||
"libc", |
|||
"wasi", |
|||
] |
|||
|
|||
[[package]] |
|||
name = "libc" |
|||
version = "0.2.101" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" |
|||
|
|||
[[package]] |
|||
name = "ppv-lite86" |
|||
version = "0.2.10" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" |
|||
|
|||
[[package]] |
|||
name = "radium" |
|||
version = "0.5.3" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" |
|||
|
|||
[[package]] |
|||
name = "rand" |
|||
version = "0.8.4" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" |
|||
dependencies = [ |
|||
"libc", |
|||
"rand_chacha", |
|||
"rand_core", |
|||
"rand_hc", |
|||
] |
|||
|
|||
[[package]] |
|||
name = "rand_chacha" |
|||
version = "0.3.1" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" |
|||
dependencies = [ |
|||
"ppv-lite86", |
|||
"rand_core", |
|||
] |
|||
|
|||
[[package]] |
|||
name = "rand_core" |
|||
version = "0.6.3" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" |
|||
dependencies = [ |
|||
"getrandom", |
|||
] |
|||
|
|||
[[package]] |
|||
name = "rand_hc" |
|||
version = "0.3.1" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" |
|||
dependencies = [ |
|||
"rand_core", |
|||
] |
|||
|
|||
[[package]] |
|||
name = "tap" |
|||
version = "1.0.1" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" |
|||
|
|||
[[package]] |
|||
name = "wasi" |
|||
version = "0.10.2+wasi-snapshot-preview1" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" |
|||
|
|||
[[package]] |
|||
name = "whist" |
|||
version = "0.1.0" |
|||
dependencies = [ |
|||
"bitvec", |
|||
"rand", |
|||
] |
|||
|
|||
[[package]] |
|||
name = "wyz" |
|||
version = "0.2.0" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" |
@ -0,0 +1,14 @@ |
|||
[package] |
|||
name = "whist" |
|||
version = "0.1.0" |
|||
authors = ["e-dt"] |
|||
edition = "2018" |
|||
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
|||
|
|||
[dependencies] |
|||
bitvec = "0.19.5" |
|||
rand = "0.8.4" |
|||
|
|||
[profile.release] |
|||
debug = true |
@ -0,0 +1,514 @@ |
|||
#![allow(non_snake_case)] |
|||
|
|||
use bitvec::prelude::*; |
|||
use rand::prelude::*; |
|||
use std::cmp::max; |
|||
use std::io; |
|||
|
|||
#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
|||
enum Suit { |
|||
Spades, |
|||
Hearts, |
|||
Diamonds, |
|||
Clubs, |
|||
} |
|||
|
|||
#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
|||
struct Card { |
|||
suit: Suit, |
|||
rank: u8, // 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A <- 2 is 0, ... A is 12 |
|||
} |
|||
|
|||
#[derive(Debug, Clone)] |
|||
struct GameState { |
|||
player: u8, |
|||
handPlayer: u8, |
|||
trump: Option<Suit>, |
|||
hand: [Option<Card>; 13], // hand of handPlayer |
|||
ruffs: bitarr!(for 16), |
|||
played: bitarr!(for 52), |
|||
trick: [Option<Card>; 3], |
|||
tricksWonBy0: u8, |
|||
sizes: [u8; 4], |
|||
} |
|||
|
|||
#[derive(Debug, Clone)] |
|||
struct Node { |
|||
visited: u64, |
|||
value: i64, |
|||
state: GameState, |
|||
outcomes: [Option<Box<Node>>; 52], |
|||
} |
|||
fn rankOf(c: Card) -> u8 { |
|||
c.rank |
|||
} |
|||
fn suitOf(c: Card) -> Suit { |
|||
c.suit |
|||
} |
|||
fn toSuit(n: u8) -> Suit { |
|||
match n { |
|||
0 => Suit::Spades, |
|||
1 => Suit::Hearts, |
|||
2 => Suit::Diamonds, |
|||
3 => Suit::Clubs, |
|||
_ => panic!("ERR in toSuit - it was given too big a number. We don't use 'fairy suits' in Whist, you goddamn idiot!"), |
|||
} |
|||
} |
|||
|
|||
fn fromSuit(s: Suit) -> u8 { |
|||
match s { |
|||
Suit::Spades => 0, |
|||
Suit::Hearts => 1, |
|||
Suit::Diamonds => 2, |
|||
Suit::Clubs => 3, |
|||
} |
|||
} |
|||
|
|||
fn numToCard(num: u8) -> Card { |
|||
// this assumes the number is legal. |
|||
let rank: u8 = num % 13; |
|||
let suit: Suit = toSuit(num / 13); |
|||
Card { suit, rank } |
|||
} |
|||
|
|||
fn cardToNum(card: Card) -> u8 { |
|||
// this assumes card rank is legal. |
|||
let suitN: u8 = fromSuit(suitOf(card)); |
|||
suitN * 13 + rankOf(card) |
|||
} |
|||
|
|||
fn state_transit(state: &GameState, action: Card) -> GameState { |
|||
// this assumes the action is legal. Please don't do illegal action? |
|||
let hand: [Option<Card>; 13] = if state.player == state.handPlayer { |
|||
let mut new: [Option<Card>; 13] = state.hand; |
|||
for item in &mut new { |
|||
// this assumes action is in hand |
|||
if let Some(x) = item { |
|||
if *x == action { |
|||
*item = None; |
|||
} |
|||
} |
|||
} |
|||
new |
|||
} else { |
|||
state.hand |
|||
}; |
|||
|
|||
let mut played: bitarr!(for 52) = state.played; |
|||
played.set(cardToNum(action).into(), true); |
|||
|
|||
let ruffs: bitarr!(for 16) = match state.trick[0] { |
|||
Some(firstCard) => { |
|||
if suitOf(action) != suitOf(firstCard) { |
|||
let mut new: bitarr!(for 16) = state.ruffs; |
|||
new.set( |
|||
(state.player * 4 + fromSuit(suitOf(firstCard))).into(), |
|||
true, |
|||
); |
|||
new |
|||
} else { |
|||
state.ruffs |
|||
} |
|||
} |
|||
None => state.ruffs, |
|||
}; |
|||
|
|||
if state.trick[2] != None { |
|||
// Ok, new trick after this. |
|||
|
|||
let firstCard: Card = state.trick[0].unwrap(); //this assumes that tricks is sane |
|||
let mut maxRank: u8 = rankOf(action); |
|||
let mut trumped: bool = Some(suitOf(action)) == state.trump; |
|||
let mut winner: u8 = state.player; |
|||
for (i, card) in state.trick.iter().enumerate() { |
|||
match card { |
|||
Some(x) => { |
|||
if (suitOf(*x) == suitOf(firstCard) && !trumped && rankOf(*x) > maxRank) |
|||
|| (Some(suitOf(*x)) == state.trump && (!trumped || rankOf(*x) > maxRank)) |
|||
{ |
|||
maxRank = rankOf(*x); |
|||
winner = (8 + i as u8 - (3 - state.player)) % 4; |
|||
if Some(suitOf(*x)) == state.trump { |
|||
trumped = true; |
|||
} |
|||
} |
|||
} |
|||
None => (), |
|||
} |
|||
} |
|||
let newWon: u8 = state.tricksWonBy0 + if winner == 0 || winner == 2 { 1 } else { 0 }; |
|||
let mut sizes = state.sizes; |
|||
sizes[usize::from(state.player)] -= 1; |
|||
GameState { |
|||
player: winner, |
|||
handPlayer: state.handPlayer, |
|||
trump: state.trump, |
|||
hand, |
|||
ruffs, |
|||
played, |
|||
trick: [None, None, None], |
|||
tricksWonBy0: newWon, |
|||
sizes, |
|||
} |
|||
} else { |
|||
let mut trick: [Option<Card>; 3] = state.trick; |
|||
for i in 0..3 { |
|||
if trick[i] == None { |
|||
trick[i] = Some(action); |
|||
break; |
|||
} |
|||
} |
|||
let mut sizes = state.sizes; |
|||
sizes[usize::from(state.player)] -= 1; |
|||
GameState { |
|||
player: (state.player + 1) % 4, |
|||
handPlayer: state.handPlayer, |
|||
trump: state.trump, |
|||
hand, |
|||
ruffs, |
|||
played, |
|||
trick, |
|||
tricksWonBy0: state.tricksWonBy0, |
|||
sizes, |
|||
} |
|||
} |
|||
} |
|||
|
|||
fn card(suit: Suit, rank: u8) -> Card { |
|||
Card { suit, rank } |
|||
} |
|||
|
|||
fn new_node(state: GameState) -> Node { |
|||
Node { |
|||
visited: 0, |
|||
value: 0, |
|||
state, |
|||
outcomes: [ |
|||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, |
|||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, |
|||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, |
|||
None, None, None, None, None, None, None, None, None, None, |
|||
], |
|||
} |
|||
} |
|||
|
|||
fn hands(state: &GameState) -> [[Option<u8>; 13]; 4] { |
|||
let mut hand: [[Option<u8>; 13] 4] = [vec![], vec![], vec![], vec![]]; |
|||
let mut seen = state.played; |
|||
for i in state.hand.iter() { |
|||
match i { |
|||
None => (), |
|||
Some(x) => { |
|||
hand[usize::from(state.handPlayer)].push(cardToNum(*x)); |
|||
seen.set(cardToNum(*x).into(), true); |
|||
} |
|||
} |
|||
} |
|||
let mut sizes = state.sizes; |
|||
let mut allowed: [[bool; 4]; 52] = [[true, true, true, true]; 52]; |
|||
let mut n_to_assign = 52 - seen.count_ones(); |
|||
let mut n_assignable_to = [0, 0, 0, 0]; |
|||
for i in 0..52 { |
|||
if seen[i] { |
|||
continue; |
|||
} |
|||
allowed[i][usize::from(state.handPlayer)] = false; |
|||
for j in 0..4 { |
|||
if state.ruffs[j * 4 + i / 13] == true { |
|||
allowed[i][j] = false; |
|||
} |
|||
} |
|||
let mut n = 0; |
|||
let mut last = 0; |
|||
for j in 0..4 { |
|||
if allowed[i][j] { |
|||
n += 1; |
|||
last = j; |
|||
n_assignable_to[j] += 1; |
|||
} |
|||
} |
|||
if n == 1 { |
|||
hand[last].push(i as u8); |
|||
n_to_assign -= 1; |
|||
n_assignable_to[last] -= 1; |
|||
sizes[last] -= 1; |
|||
seen.set(i, true); |
|||
} |
|||
} |
|||
let mut running = 0; |
|||
while n_to_assign > 0 { |
|||
let mut any_done = false; |
|||
for i in 0..4 { |
|||
if sizes[i] > 0 && n_assignable_to[i] == sizes[i] { |
|||
for j in 0..52 { |
|||
if allowed[j][i] && !seen[j] { |
|||
seen.set(j, true); |
|||
hand[i].push(j as u8); |
|||
sizes[i] -= 1; |
|||
n_to_assign -= 1; |
|||
for q in 0..4 { |
|||
if allowed[j][q] { |
|||
n_assignable_to[q] -= 1; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
any_done = true; |
|||
} |
|||
} |
|||
if any_done { |
|||
continue; |
|||
} |
|||
while seen[running] { |
|||
running += 1; |
|||
} |
|||
let mut n: usize = allowed[running] |
|||
.iter() |
|||
.map(|&x| if x { 1 } else { 0 }) |
|||
.sum(); |
|||
for j in 0..4 { |
|||
if sizes[j] == 0 && allowed[running][j] == true { |
|||
n -= 1; |
|||
} |
|||
} |
|||
|
|||
for j in 0..4 { |
|||
if !allowed[running][j] || sizes[j] == 0 { |
|||
continue; |
|||
} |
|||
if random::<usize>() % n == 0 { |
|||
hand[j].push(running as u8); |
|||
for i in 0..4 { |
|||
if allowed[running][i] { |
|||
n_assignable_to[i] -= 1; |
|||
} |
|||
} |
|||
sizes[j] -= 1; |
|||
n_to_assign -= 1; |
|||
break; |
|||
} else { |
|||
n -= 1; |
|||
} |
|||
} |
|||
seen.set(running, true); |
|||
} |
|||
hand |
|||
} |
|||
|
|||
fn search(node: &mut Node) -> (Vec<usize>, &mut Node) { |
|||
let mut cur: &mut Node = node; |
|||
let mut seen: Vec<usize> = vec![]; |
|||
loop { |
|||
cur.visited += 1; |
|||
if cur.state.played.count_ones() == 52 { |
|||
// abort |
|||
return (seen, cur); |
|||
} |
|||
let mut score: f64 = -10000.0; |
|||
let mut idx: usize = 100000; |
|||
let hands = hands(&cur.state); |
|||
let mut outin: [bool; 4] = [true; 4]; |
|||
for i in hands[usize::from(cur.state.player)].iter() { |
|||
outin[usize::from(i / 13)] = false; |
|||
} |
|||
for i in hands[usize::from(cur.state.player)].iter() { |
|||
match cur.state.trick[0] { |
|||
None => (), |
|||
Some(x) => { |
|||
if !(fromSuit(suitOf(x)) == i / 13) |
|||
&& !(outin[usize::from(fromSuit(suitOf(x)))]) |
|||
{ |
|||
continue; |
|||
} |
|||
} |
|||
} |
|||
match &cur.outcomes[usize::from(*i)] { |
|||
None => { |
|||
seen.push((*i).into()); |
|||
return (seen, cur); |
|||
} |
|||
Some(x) => { |
|||
let ourscore: f64 = (x.value as f64 / (x.visited as f64 + 1.0)) |
|||
+ 6.0 * ((cur.visited as f64).ln() / (x.visited as f64 + 1.0)).sqrt(); |
|||
if ourscore > score { |
|||
idx = (*i).into(); |
|||
score = ourscore; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
match &mut cur.outcomes[idx] { |
|||
None => panic!("This will not happen. SEARCH PANIC KW!#"), |
|||
Some(x) => { |
|||
seen.push(idx); |
|||
cur = x; |
|||
} |
|||
} |
|||
//cur.outcomes[idx].clone().unwrap(); |
|||
//cur = *(cur.outcomes[idx].unwrap()); |
|||
} |
|||
} |
|||
|
|||
fn propagate(node: &mut Node, path: Vec<usize>, value: i8) { |
|||
let mut cur: &mut Node = node; |
|||
let mut sgn = -1; |
|||
for i in path { |
|||
cur.value += sgn * i64::from(value); |
|||
sgn = -sgn; |
|||
match &mut cur.outcomes[i] { |
|||
None => panic!("This will not happen. SEARCH PANIC KQ!#"), |
|||
Some(x) => { |
|||
cur = x; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
fn simulate(state: &GameState) -> i8 { |
|||
let mut cur: GameState = state.clone(); |
|||
loop { |
|||
let tricks0: u8 = cur.tricksWonBy0; |
|||
if cur.played.count_ones() == 52 { |
|||
if cur.handPlayer == 0 || cur.handPlayer == 2 { |
|||
return max(tricks0 as i8 - 6, 0) - max(7 - tricks0 as i8, 0); |
|||
} else { |
|||
return max(7 - tricks0 as i8, 0) - max(tricks0 as i8 - 6, 0); |
|||
} |
|||
} |
|||
let card: Card; |
|||
// ok. find action |
|||
let hands = hands(&cur); |
|||
|
|||
let mut outin: [u8; 4] = [0; 4]; |
|||
for i in hands[usize::from(cur.player)].iter() { |
|||
outin[usize::from(i / 13)] += 1; |
|||
} |
|||
// i fucked up my life on this one |
|||
let mut n: u8 = cur.sizes[usize::from(cur.player)]; |
|||
let mut follow: bool = false; |
|||
let mut led: u8 = 100; |
|||
match cur.trick[0] { |
|||
None => (), |
|||
Some(x) => { |
|||
if outin[usize::from(fromSuit(suitOf(x)))] > 0 { |
|||
n = outin[usize::from(fromSuit(suitOf(x)))]; |
|||
led = fromSuit(suitOf(x)); |
|||
follow = true; |
|||
} |
|||
} |
|||
} |
|||
let mut p: Card = Card { |
|||
suit: Suit::Diamonds, |
|||
rank: 105, |
|||
}; // see this, something has gone terribly wrong. |
|||
for i in hands[usize::from(cur.player)].iter() { |
|||
if follow && led != i / 13 { |
|||
continue; |
|||
} |
|||
if random::<u8>() % n == 0 { |
|||
p = numToCard(*i); |
|||
break; |
|||
} else { |
|||
n -= 1; |
|||
} |
|||
} |
|||
|
|||
card = p; |
|||
cur = state_transit(&cur, card); |
|||
} |
|||
} |
|||
|
|||
fn main() { |
|||
let mut played = bitarr!(0; 52); |
|||
played.set(12, true); |
|||
played.set(1, true); |
|||
played.set(0, true); |
|||
let mut node = new_node(GameState { |
|||
player: 0, |
|||
handPlayer: 0, |
|||
trump: Some(Suit::Hearts), |
|||
hand: [ |
|||
Some(card(Suit::Hearts, 0)), |
|||
Some(card(Suit::Hearts, 2)), |
|||
Some(card(Suit::Hearts, 7)), |
|||
Some(card(Suit::Hearts, 8)), |
|||
Some(card(Suit::Hearts, 9)), |
|||
Some(card(Suit::Hearts, 10)), |
|||
Some(card(Suit::Spades, 2)), |
|||
Some(card(Suit::Spades, 7)), |
|||
Some(card(Suit::Spades, 10)), |
|||
Some(card(Suit::Diamonds, 2)), |
|||
Some(card(Suit::Clubs, 1)), |
|||
Some(card(Suit::Clubs, 3)), |
|||
Some(card(Suit::Clubs, 7)), |
|||
], |
|||
ruffs: bitarr!(0; 16), |
|||
played, |
|||
trick: [ |
|||
Some(card(Suit::Spades, 12)), |
|||
Some(card(Suit::Spades, 1)), |
|||
Some(card(Suit::Spades, 0)), |
|||
], |
|||
tricksWonBy0: 0, |
|||
sizes: [13, 12, 12, 12], |
|||
}); |
|||
loop { |
|||
if node.state.player != node.state.handPlayer { |
|||
println!("Input the card played by player {}", node.state.player); |
|||
let mut n = String::new(); |
|||
io::stdin().read_line(&mut n).expect("Input!"); |
|||
let card: usize = n.trim().parse().expect("RIGHT!"); |
|||
match &node.outcomes[card] { |
|||
None => { |
|||
node = new_node(state_transit(&node.state, numToCard(card as u8))); |
|||
} |
|||
Some(x) => { |
|||
node = (**x).clone(); |
|||
} |
|||
} |
|||
continue; |
|||
} |
|||
for _ in 1..10000 { |
|||
let pathcur: (Vec<usize>, &mut Node) = search(&mut node); //search! |
|||
let path = pathcur.0; |
|||
let cur = pathcur.1; |
|||
let n: i8; |
|||
if cur.state.played.count_ones() == 52 { |
|||
let tricks0: u8 = cur.state.tricksWonBy0; |
|||
n = if cur.state.handPlayer == 0 || cur.state.handPlayer == 2 { |
|||
max(tricks0 as i8 - 6, 0) - max(7 - tricks0 as i8, 0) |
|||
} else { |
|||
max(7 - tricks0 as i8, 0) - max(tricks0 as i8 - 6, 0) |
|||
} |
|||
} else { |
|||
cur.outcomes[path[path.len() - 1]] = Some(Box::new(new_node(state_transit( |
|||
&cur.state, |
|||
numToCard(path[path.len() - 1] as u8), |
|||
)))); //expand! |
|||
n = simulate(&(*cur.outcomes[path[path.len() - 1]].as_ref().unwrap()).state); |
|||
//simulate! |
|||
} |
|||
propagate(&mut node, path, n); |
|||
} |
|||
let mut highest_value = -4000.0; |
|||
let mut best_move = 100; |
|||
for i in 0..52 { |
|||
match &node.outcomes[i] { |
|||
None => (), |
|||
Some(x) => { |
|||
if x.value as f64 / x.visited as f64 > highest_value { |
|||
highest_value = x.value as f64 / x.visited as f64; |
|||
best_move = i; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
println!("{:?}", numToCard(best_move as u8)); |
|||
break; |
|||
match &node.outcomes[best_move] { |
|||
None => panic!("ok"), |
|||
Some(x) => { |
|||
node = (**x).clone(); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,537 @@ |
|||
#![allow(non_snake_case)] |
|||
|
|||
use bitvec::prelude::*; |
|||
use rand::prelude::*; |
|||
use std::cmp::max; |
|||
use std::io; |
|||
|
|||
#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
|||
enum Suit { |
|||
Spades, |
|||
Hearts, |
|||
Diamonds, |
|||
Clubs, |
|||
} |
|||
|
|||
#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
|||
struct Card { |
|||
suit: Suit, |
|||
rank: u8, // 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A <- 2 is 0, ... A is 12
|
|||
} |
|||
|
|||
#[derive(Debug, Clone)] |
|||
struct GameState { |
|||
player: u8, |
|||
handPlayer: u8, |
|||
trump: Option<Suit>, |
|||
hand: [Option<Card>; 13], // hand of handPlayer
|
|||
ruffs: bitarr!(for 16), |
|||
played: bitarr!(for 52), |
|||
trick: [Option<Card>; 3], |
|||
tricksWonBy0: u8, |
|||
sizes: [u8; 4], |
|||
} |
|||
|
|||
#[derive(Debug, Clone)] |
|||
struct Node { |
|||
visited: u64, |
|||
value: i64, |
|||
state: GameState, |
|||
outcomes: [Option<Box<Node>>; 52], |
|||
} |
|||
fn rankOf(c: Card) -> u8 { |
|||
c.rank |
|||
} |
|||
fn suitOf(c: Card) -> Suit { |
|||
c.suit |
|||
} |
|||
fn toSuit(n: u8) -> Suit { |
|||
match n { |
|||
0 => Suit::Spades, |
|||
1 => Suit::Hearts, |
|||
2 => Suit::Diamonds, |
|||
3 => Suit::Clubs, |
|||
_ => panic!("ERR in toSuit - it was given too big a number. We don't use 'fairy suits' in Whist, you goddamn idiot!"), |
|||
} |
|||
} |
|||
|
|||
fn fromSuit(s: Suit) -> u8 { |
|||
match s { |
|||
Suit::Spades => 0, |
|||
Suit::Hearts => 1, |
|||
Suit::Diamonds => 2, |
|||
Suit::Clubs => 3, |
|||
} |
|||
} |
|||
|
|||
fn numToCard(num: u8) -> Card { |
|||
// this assumes the number is legal.
|
|||
let rank: u8 = num % 13; |
|||
let suit: Suit = toSuit(num / 13); |
|||
Card { suit, rank } |
|||
} |
|||
|
|||
fn cardToNum(card: Card) -> u8 { |
|||
// this assumes card rank is legal.
|
|||
let suitN: u8 = fromSuit(suitOf(card)); |
|||
suitN * 13 + rankOf(card) |
|||
} |
|||
|
|||
fn state_transit(state: &GameState, action: Card) -> GameState { |
|||
// this assumes the action is legal. Please don't do illegal action?
|
|||
let hand: [Option<Card>; 13] = if state.player == state.handPlayer { |
|||
let mut new: [Option<Card>; 13] = state.hand; |
|||
for item in &mut new { |
|||
// this assumes action is in hand
|
|||
if let Some(x) = item { |
|||
if *x == action { |
|||
*item = None; |
|||
} |
|||
} |
|||
} |
|||
new |
|||
} else { |
|||
state.hand |
|||
}; |
|||
|
|||
let mut played: bitarr!(for 52) = state.played; |
|||
played.set(cardToNum(action).into(), true); |
|||
|
|||
let ruffs: bitarr!(for 16) = match state.trick[0] { |
|||
Some(firstCard) => { |
|||
if suitOf(action) != suitOf(firstCard) { |
|||
let mut new: bitarr!(for 16) = state.ruffs; |
|||
new.set( |
|||
(state.player * 4 + fromSuit(suitOf(firstCard))).into(), |
|||
true, |
|||
); |
|||
new |
|||
} else { |
|||
state.ruffs |
|||
} |
|||
} |
|||
None => state.ruffs, |
|||
}; |
|||
|
|||
if state.trick[2] != None { |
|||
// Ok, new trick after this.
|
|||
|
|||
let firstCard: Card = state.trick[0].unwrap(); //this assumes that tricks is sane
|
|||
let mut maxRank: u8 = rankOf(action); |
|||
let mut trumped: bool = Some(suitOf(action)) == state.trump; |
|||
let mut winner: u8 = state.player; |
|||
for (i, card) in state.trick.iter().enumerate() { |
|||
match card { |
|||
Some(x) => { |
|||
if (suitOf(*x) == suitOf(firstCard) && !trumped && rankOf(*x) > maxRank) |
|||
|| (Some(suitOf(*x)) == state.trump && (!trumped || rankOf(*x) > maxRank)) |
|||
{ |
|||
maxRank = rankOf(*x); |
|||
winner = (8 + i as u8 - (3 - state.player)) % 4; |
|||
if Some(suitOf(*x)) == state.trump { |
|||
trumped = true; |
|||
} |
|||
} |
|||
} |
|||
None => (), |
|||
} |
|||
} |
|||
let newWon: u8 = state.tricksWonBy0 + if winner == 0 || winner == 2 { 1 } else { 0 }; |
|||
let mut sizes = state.sizes; |
|||
sizes[usize::from(state.player)] -= 1; |
|||
GameState { |
|||
player: winner, |
|||
handPlayer: state.handPlayer, |
|||
trump: state.trump, |
|||
hand, |
|||
ruffs, |
|||
played, |
|||
trick: [None, None, None], |
|||
tricksWonBy0: newWon, |
|||
sizes, |
|||
} |
|||
} else { |
|||
let mut trick: [Option<Card>; 3] = state.trick; |
|||
for i in 0..3 { |
|||
if trick[i] == None { |
|||
trick[i] = Some(action); |
|||
break; |
|||
} |
|||
} |
|||
let mut sizes = state.sizes; |
|||
sizes[usize::from(state.player)] -= 1; |
|||
GameState { |
|||
player: (state.player + 1) % 4, |
|||
handPlayer: state.handPlayer, |
|||
trump: state.trump, |
|||
hand, |
|||
ruffs, |
|||
played, |
|||
trick, |
|||
tricksWonBy0: state.tricksWonBy0, |
|||
sizes, |
|||
} |
|||
} |
|||
} |
|||
|
|||
fn card(suit: Suit, rank: u8) -> Card { |
|||
Card { suit, rank } |
|||
} |
|||
|
|||
fn new_node(state: GameState) -> Node { |
|||
Node { |
|||
visited: 0, |
|||
value: 0, |
|||
state, |
|||
outcomes: [ |
|||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, |
|||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, |
|||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, |
|||
None, None, None, None, None, None, None, None, None, None, |
|||
], |
|||
} |
|||
} |
|||
|
|||
fn hands(state: &GameState) -> [Vec<u8>; 4] { |
|||
let mut hand: [Vec<u8>; 4] = [vec![], vec![], vec![], vec![]]; |
|||
let mut seen = state.played; |
|||
for i in state.hand.iter() { |
|||
match i { |
|||
None => (), |
|||
Some(x) => { |
|||
hand[usize::from(state.handPlayer)].push(cardToNum(*x)); |
|||
seen.set(cardToNum(*x).into(), true); |
|||
} |
|||
} |
|||
} |
|||
let mut sizes = state.sizes; |
|||
let mut allowed: [[bool; 4]; 52] = [[true, true, true, true]; 52]; |
|||
let mut n_to_assign = 52 - seen.count_ones(); |
|||
let mut n_assignable_to = [0, 0, 0, 0]; |
|||
for i in 0..52 { |
|||
if seen[i] { |
|||
continue; |
|||
} |
|||
allowed[i][usize::from(state.handPlayer)] = false; |
|||
for j in 0..4 { |
|||
if state.ruffs[j * 4 + i / 13] == true { |
|||
allowed[i][j] = false; |
|||
} |
|||
} |
|||
let mut n = 0; |
|||
let mut last = 0; |
|||
for j in 0..4 { |
|||
if allowed[i][j] { |
|||
n += 1; |
|||
last = j; |
|||
n_assignable_to[j] += 1; |
|||
} |
|||
} |
|||
if n == 1 { |
|||
hand[last].push(i as u8); |
|||
n_to_assign -= 1; |
|||
n_assignable_to[last] -= 1; |
|||
sizes[last] -= 1; |
|||
seen.set(i, true); |
|||
} |
|||
} |
|||
let mut running = 0; |
|||
while n_to_assign > 0 { |
|||
let mut any_done = false; |
|||
/* for i in 0..52 {
|
|||
if seen[i] { |
|||
continue; |
|||
} |
|||
|
|||
let mut s = 0; |
|||
let mut last = 0; |
|||
for j in 0..4 { |
|||
if allowed[i][j] { |
|||
s += 1; |
|||
last = j; |
|||
} |
|||
} |
|||
if s == 1 { |
|||
hand[last].push(i as u8); |
|||
sizes[last] -= 1; |
|||
n_to_assign -= 1; |
|||
n_assignable_to[last] -= 1; |
|||
seen.set(i, true); |
|||
any_done = true; |
|||
} |
|||
} */ |
|||
|
|||
any_done = false; |
|||
for i in 0..4 { |
|||
if sizes[i] > 0 && n_assignable_to[i] == sizes[i] { |
|||
for j in 0..52 { |
|||
if allowed[j][i] && !seen[j] { |
|||
seen.set(j, true); |
|||
hand[i].push(j as u8); |
|||
sizes[i] -= 1; |
|||
n_to_assign -= 1; |
|||
for q in 0..4 { |
|||
if allowed[j][q] { |
|||
n_assignable_to[q] -= 1; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
any_done = true; |
|||
} |
|||
} |
|||
if any_done { |
|||
continue; |
|||
} |
|||
while seen[running] { |
|||
running += 1; |
|||
} |
|||
let mut n: usize = allowed[running] |
|||
.iter() |
|||
.map(|&x| if x { 1 } else { 0 }) |
|||
.sum(); |
|||
for j in 0..4 { |
|||
if sizes[j] == 0 && allowed[running][j] == true { |
|||
n -= 1; |
|||
} |
|||
} |
|||
|
|||
for j in 0..4 { |
|||
if !allowed[running][j] || sizes[j] == 0 { |
|||
continue; |
|||
} |
|||
if random::<usize>() % n == 0 { |
|||
hand[j].push(running as u8); |
|||
for i in 0..4 { |
|||
if allowed[running][i] { |
|||
n_assignable_to[i] -= 1; |
|||
} |
|||
} |
|||
sizes[j] -= 1; |
|||
n_to_assign -= 1; |
|||
break; |
|||
} else { |
|||
n -= 1; |
|||
} |
|||
} |
|||
seen.set(running, true); |
|||
} |
|||
hand |
|||
} |
|||
|
|||
fn search(node: &mut Node) -> (Vec<usize>, &mut Node) { |
|||
let mut cur: &mut Node = node; |
|||
let mut seen: Vec<usize> = vec![]; |
|||
loop { |
|||
cur.visited += 1; |
|||
if cur.state.played.count_ones() == 52 { |
|||
// abort
|
|||
return (seen, cur); |
|||
} |
|||
let mut score: f64 = -10000.0; |
|||
let mut idx: usize = 100000; |
|||
let hands = hands(&cur.state); |
|||
let mut outin: [bool; 4] = [true; 4]; |
|||
for i in hands[usize::from(cur.state.player)].iter() { |
|||
outin[usize::from(i / 13)] = false; |
|||
} |
|||
for i in hands[usize::from(cur.state.player)].iter() { |
|||
match cur.state.trick[0] { |
|||
None => (), |
|||
Some(x) => { |
|||
if !(fromSuit(suitOf(x)) == i / 13) |
|||
&& !(outin[usize::from(fromSuit(suitOf(x)))]) |
|||
{ |
|||
continue; |
|||
} |
|||
} |
|||
} |
|||
match &cur.outcomes[usize::from(*i)] { |
|||
None => { |
|||
seen.push((*i).into()); |
|||
return (seen, cur); |
|||
} |
|||
Some(x) => { |
|||
let ourscore: f64 = (x.value as f64 / (x.visited as f64 + 1.0)) |
|||
+ 6.0 * ((cur.visited as f64).ln() / (x.visited as f64 + 1.0)).sqrt(); |
|||
if ourscore > score { |
|||
idx = (*i).into(); |
|||
score = ourscore; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
match &mut cur.outcomes[idx] { |
|||
None => panic!("This will not happen. SEARCH PANIC KW!#"), |
|||
Some(x) => { |
|||
seen.push(idx); |
|||
cur = x; |
|||
} |
|||
} |
|||
//cur.outcomes[idx].clone().unwrap();
|
|||
//cur = *(cur.outcomes[idx].unwrap());
|
|||
} |
|||
} |
|||
|
|||
fn propagate(node: &mut Node, path: Vec<usize>, value: i8) { |
|||
let mut cur: &mut Node = node; |
|||
let mut sgn = -1; |
|||
for i in path { |
|||
cur.value += sgn * i64::from(value); |
|||
sgn = -sgn; |
|||
match &mut cur.outcomes[i] { |
|||
None => panic!("This will not happen. SEARCH PANIC KQ!#"), |
|||
Some(x) => { |
|||
cur = x; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
fn simulate(state: &GameState) -> i8 { |
|||
let mut cur: GameState = state.clone(); |
|||
loop { |
|||
let tricks0: u8 = cur.tricksWonBy0; |
|||
if cur.played.count_ones() == 52 { |
|||
if cur.handPlayer == 0 || cur.handPlayer == 2 { |
|||
return max(tricks0 as i8 - 6, 0) - max(7 - tricks0 as i8, 0); |
|||
} else { |
|||
return max(7 - tricks0 as i8, 0) - max(tricks0 as i8 - 6, 0); |
|||
} |
|||
} |
|||
let card: Card; |
|||
// ok. find action
|
|||
let hands = hands(&cur); |
|||
|
|||
let mut outin: [u8; 4] = [0; 4]; |
|||
for i in hands[usize::from(cur.player)].iter() { |
|||
outin[usize::from(i / 13)] += 1; |
|||
} |
|||
// i fucked up my life on this one
|
|||
let mut n: u8 = cur.sizes[usize::from(cur.player)]; |
|||
let mut follow: bool = false; |
|||
let mut led: u8 = 100; |
|||
match cur.trick[0] { |
|||
None => (), |
|||
Some(x) => { |
|||
if outin[usize::from(fromSuit(suitOf(x)))] > 0 { |
|||
n = outin[usize::from(fromSuit(suitOf(x)))]; |
|||
led = fromSuit(suitOf(x)); |
|||
follow = true; |
|||
} |
|||
} |
|||
} |
|||
let mut p: Card = Card { |
|||
suit: Suit::Diamonds, |
|||
rank: 105, |
|||
}; // see this, something has gone terribly wrong.
|
|||
for i in hands[usize::from(cur.player)].iter() { |
|||
if follow && led != i / 13 { |
|||
continue; |
|||
} |
|||
if random::<u8>() % n == 0 { |
|||
p = numToCard(*i); |
|||
break; |
|||
} else { |
|||
n -= 1; |
|||
} |
|||
} |
|||
|
|||
card = p; |
|||
cur = state_transit(&cur, card); |
|||
} |
|||
} |
|||
|
|||
fn main() { |
|||
let mut played = bitarr!(0; 52); |
|||
played.set(12, true); |
|||
played.set(1, true); |
|||
played.set(0, true); |
|||
let mut node = new_node(GameState { |
|||
player: 0, |
|||
handPlayer: 0, |
|||
trump: Some(Suit::Hearts), |
|||
hand: [ |
|||
Some(card(Suit::Hearts, 0)), |
|||
Some(card(Suit::Hearts, 2)), |
|||
Some(card(Suit::Hearts, 7)), |
|||
Some(card(Suit::Hearts, 8)), |
|||
Some(card(Suit::Hearts, 9)), |
|||
Some(card(Suit::Hearts, 10)), |
|||
Some(card(Suit::Spades, 2)), |
|||
Some(card(Suit::Spades, 7)), |
|||
Some(card(Suit::Spades, 10)), |
|||
Some(card(Suit::Diamonds, 2)), |
|||
Some(card(Suit::Clubs, 1)), |
|||
Some(card(Suit::Clubs, 3)), |
|||
Some(card(Suit::Clubs, 7)), |
|||
], |
|||
ruffs: bitarr!(0; 16), |
|||
played, |
|||
trick: [ |
|||
Some(card(Suit::Spades, 12)), |
|||
Some(card(Suit::Spades, 1)), |
|||
Some(card(Suit::Spades, 0)), |
|||
], |
|||
tricksWonBy0: 0, |
|||
sizes: [13, 12, 12, 12], |
|||
}); |
|||
loop { |
|||
if node.state.player != node.state.handPlayer { |
|||
println!("Input the card played by player {}", node.state.player); |
|||
let mut n = String::new(); |
|||
io::stdin().read_line(&mut n).expect("Input!"); |
|||
let card: usize = n.trim().parse().expect("RIGHT!"); |
|||
match &node.outcomes[card] { |
|||
None => { |
|||
node = new_node(state_transit(&node.state, numToCard(card as u8))); |
|||
} |
|||
Some(x) => { |
|||
node = (**x).clone(); |
|||
} |
|||
} |
|||
continue; |
|||
} |
|||
for _ in 1..10000 { |
|||
let pathcur: (Vec<usize>, &mut Node) = search(&mut node); //search!
|
|||
let path = pathcur.0; |
|||
let cur = pathcur.1; |
|||
let n: i8; |
|||
if cur.state.played.count_ones() == 52 { |
|||
let tricks0: u8 = cur.state.tricksWonBy0; |
|||
n = if cur.state.handPlayer == 0 || cur.state.handPlayer == 2 { |
|||
max(tricks0 as i8 - 6, 0) - max(7 - tricks0 as i8, 0) |
|||
} else { |
|||
max(7 - tricks0 as i8, 0) - max(tricks0 as i8 - 6, 0) |
|||
} |
|||
} else { |
|||
cur.outcomes[path[path.len() - 1]] = Some(Box::new(new_node(state_transit( |
|||
&cur.state, |
|||
numToCard(path[path.len() - 1] as u8), |
|||
)))); //expand!
|
|||
n = simulate(&(*cur.outcomes[path[path.len() - 1]].as_ref().unwrap()).state); |
|||
//simulate!
|
|||
} |
|||
propagate(&mut node, path, n); |
|||
} |
|||
let mut highest_value = -4000.0; |
|||
let mut best_move = 100; |
|||
for i in 0..52 { |
|||
match &node.outcomes[i] { |
|||
None => (), |
|||
Some(x) => { |
|||
if x.value as f64 / x.visited as f64 > highest_value { |
|||
highest_value = x.value as f64 / x.visited as f64; |
|||
best_move = i; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
println!("{:?}", numToCard(best_move as u8)); |
|||
match &node.outcomes[best_move] { |
|||
None => panic!("ok"), |
|||
Some(x) => { |
|||
node = (**x).clone(); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,514 @@ |
|||
#![allow(non_snake_case)] |
|||
|
|||
use bitvec::prelude::*; |
|||
use rand::prelude::*; |
|||
use std::cmp::max; |
|||
use std::io; |
|||
|
|||
#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
|||
enum Suit { |
|||
Spades, |
|||
Hearts, |
|||
Diamonds, |
|||
Clubs, |
|||
} |
|||
|
|||
#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
|||
struct Card { |
|||
suit: Suit, |
|||
rank: u8, // 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A <- 2 is 0, ... A is 12
|
|||
} |
|||
|
|||
#[derive(Debug, Clone)] |
|||
struct GameState { |
|||
player: u8, |
|||
handPlayer: u8, |
|||
trump: Option<Suit>, |
|||
hand: [Option<Card>; 13], // hand of handPlayer
|
|||
ruffs: bitarr!(for 16), |
|||
played: bitarr!(for 52), |
|||
trick: [Option<Card>; 3], |
|||
tricksWonBy0: u8, |
|||
sizes: [u8; 4], |
|||
} |
|||
|
|||
#[derive(Debug, Clone)] |
|||
struct Node { |
|||
visited: u64, |
|||
value: i64, |
|||
state: GameState, |
|||
outcomes: [Option<Box<Node>>; 52], |
|||
} |
|||
fn rankOf(c: Card) -> u8 { |
|||
c.rank |
|||
} |
|||
fn suitOf(c: Card) -> Suit { |
|||
c.suit |
|||
} |
|||
fn toSuit(n: u8) -> Suit { |
|||
match n { |
|||
0 => Suit::Spades, |
|||
1 => Suit::Hearts, |
|||
2 => Suit::Diamonds, |
|||
3 => Suit::Clubs, |
|||
_ => panic!("ERR in toSuit - it was given too big a number. We don't use 'fairy suits' in Whist, you goddamn idiot!"), |
|||
} |
|||
} |
|||
|
|||
fn fromSuit(s: Suit) -> u8 { |
|||
match s { |
|||
Suit::Spades => 0, |
|||
Suit::Hearts => 1, |
|||
Suit::Diamonds => 2, |
|||
Suit::Clubs => 3, |
|||
} |
|||
} |
|||
|
|||
fn numToCard(num: u8) -> Card { |
|||
// this assumes the number is legal.
|
|||
let rank: u8 = num % 13; |
|||
let suit: Suit = toSuit(num / 13); |
|||
Card { suit, rank } |
|||
} |
|||
|
|||
fn cardToNum(card: Card) -> u8 { |
|||
// this assumes card rank is legal.
|
|||
let suitN: u8 = fromSuit(suitOf(card)); |
|||
suitN * 13 + rankOf(card) |
|||
} |
|||
|
|||
fn state_transit(state: &GameState, action: Card) -> GameState { |
|||
// this assumes the action is legal. Please don't do illegal action?
|
|||
let hand: [Option<Card>; 13] = if state.player == state.handPlayer { |
|||
let mut new: [Option<Card>; 13] = state.hand; |
|||
for item in &mut new { |
|||
// this assumes action is in hand
|
|||
if let Some(x) = item { |
|||
if *x == action { |
|||
*item = None; |
|||
} |
|||
} |
|||
} |
|||
new |
|||
} else { |
|||
state.hand |
|||
}; |
|||
|
|||
let mut played: bitarr!(for 52) = state.played; |
|||
played.set(cardToNum(action).into(), true); |
|||
|
|||
let ruffs: bitarr!(for 16) = match state.trick[0] { |
|||
Some(firstCard) => { |
|||
if suitOf(action) != suitOf(firstCard) { |
|||
let mut new: bitarr!(for 16) = state.ruffs; |
|||
new.set( |
|||
(state.player * 4 + fromSuit(suitOf(firstCard))).into(), |
|||
true, |
|||
); |
|||
new |
|||
} else { |
|||
state.ruffs |
|||
} |
|||
} |
|||
None => state.ruffs, |
|||
}; |
|||
|
|||
if state.trick[2] != None { |
|||
// Ok, new trick after this.
|
|||
|
|||
let firstCard: Card = state.trick[0].unwrap(); //this assumes that tricks is sane
|
|||
let mut maxRank: u8 = rankOf(action); |
|||
let mut trumped: bool = Some(suitOf(action)) == state.trump; |
|||
let mut winner: u8 = state.player; |
|||
for (i, card) in state.trick.iter().enumerate() { |
|||
match card { |
|||
Some(x) => { |
|||
if (suitOf(*x) == suitOf(firstCard) && !trumped && rankOf(*x) > maxRank) |
|||
|| (Some(suitOf(*x)) == state.trump && (!trumped || rankOf(*x) > maxRank)) |
|||
{ |
|||
maxRank = rankOf(*x); |
|||
winner = (8 + i as u8 - (3 - state.player)) % 4; |
|||
if Some(suitOf(*x)) == state.trump { |
|||
trumped = true; |
|||
} |
|||
} |
|||
} |
|||
None => (), |
|||
} |
|||
} |
|||
let newWon: u8 = state.tricksWonBy0 + if winner == 0 || winner == 2 { 1 } else { 0 }; |
|||
let mut sizes = state.sizes; |
|||
sizes[usize::from(state.player)] -= 1; |
|||
GameState { |
|||
player: winner, |
|||
handPlayer: state.handPlayer, |
|||
trump: state.trump, |
|||
hand, |
|||
ruffs, |
|||
played, |
|||
trick: [None, None, None], |
|||
tricksWonBy0: newWon, |
|||
sizes, |
|||
} |
|||
} else { |
|||
let mut trick: [Option<Card>; 3] = state.trick; |
|||
for i in 0..3 { |
|||
if trick[i] == None { |
|||
trick[i] = Some(action); |
|||
break; |
|||
} |
|||
} |
|||
let mut sizes = state.sizes; |
|||
sizes[usize::from(state.player)] -= 1; |
|||
GameState { |
|||
player: (state.player + 1) % 4, |
|||
handPlayer: state.handPlayer, |
|||
trump: state.trump, |
|||
hand, |
|||
ruffs, |
|||
played, |
|||
trick, |
|||
tricksWonBy0: state.tricksWonBy0, |
|||
sizes, |
|||
} |
|||
} |
|||
} |
|||
|
|||
fn card(suit: Suit, rank: u8) -> Card { |
|||
Card { suit, rank } |
|||
} |
|||
|
|||
fn new_node(state: GameState) -> Node { |
|||
Node { |
|||
visited: 0, |
|||
value: 0, |
|||
state, |
|||
outcomes: [ |
|||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, |
|||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, |
|||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, |
|||
None, None, None, None, None, None, None, None, None, None, |
|||
], |
|||
} |
|||
} |
|||
|
|||
fn hands(state: &GameState) -> [[Option<u8>; 13]; 4] { |
|||
let mut hand: [[Option<u8>; 13] 4] = [vec![], vec![], vec![], vec![]]; |
|||
let mut seen = state.played; |
|||
for i in state.hand.iter() { |
|||
match i { |
|||
None => (), |
|||
Some(x) => { |
|||
hand[usize::from(state.handPlayer)].push(cardToNum(*x)); |
|||
seen.set(cardToNum(*x).into(), true); |
|||
} |
|||
} |
|||
} |
|||
let mut sizes = state.sizes; |
|||
let mut allowed: [[bool; 4]; 52] = [[true, true, true, true]; 52]; |
|||
let mut n_to_assign = 52 - seen.count_ones(); |
|||
let mut n_assignable_to = [0, 0, 0, 0]; |
|||
for i in 0..52 { |
|||
if seen[i] { |
|||
continue; |
|||
} |
|||
allowed[i][usize::from(state.handPlayer)] = false; |
|||
for j in 0..4 { |
|||
if state.ruffs[j * 4 + i / 13] == true { |
|||
allowed[i][j] = false; |
|||
} |
|||
} |
|||
let mut n = 0; |
|||
let mut last = 0; |
|||
for j in 0..4 { |
|||
if allowed[i][j] { |
|||
n += 1; |
|||
last = j; |
|||
n_assignable_to[j] += 1; |
|||
} |
|||
} |
|||
if n == 1 { |
|||
hand[last].push(i as u8); |
|||
n_to_assign -= 1; |
|||
n_assignable_to[last] -= 1; |
|||
sizes[last] -= 1; |
|||
seen.set(i, true); |
|||
} |
|||
} |
|||
let mut running = 0; |
|||
while n_to_assign > 0 { |
|||
let mut any_done = false; |
|||
for i in 0..4 { |
|||
if sizes[i] > 0 && n_assignable_to[i] == sizes[i] { |
|||
for j in 0..52 { |
|||
if allowed[j][i] && !seen[j] { |
|||
seen.set(j, true); |
|||
hand[i].push(j as u8); |
|||
sizes[i] -= 1; |
|||
n_to_assign -= 1; |
|||
for q in 0..4 { |
|||
if allowed[j][q] { |
|||
n_assignable_to[q] -= 1; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
any_done = true; |
|||
} |
|||
} |
|||
if any_done { |
|||
continue; |
|||
} |
|||
while seen[running] { |
|||
running += 1; |
|||
} |
|||
let mut n: usize = allowed[running] |
|||
.iter() |
|||
.map(|&x| if x { 1 } else { 0 }) |
|||
.sum(); |
|||
for j in 0..4 { |
|||
if sizes[j] == 0 && allowed[running][j] == true { |
|||
n -= 1; |
|||
} |
|||
} |
|||
|
|||
for j in 0..4 { |
|||
if !allowed[running][j] || sizes[j] == 0 { |
|||
continue; |
|||
} |
|||
if random::<usize>() % n == 0 { |
|||
hand[j].push(running as u8); |
|||
for i in 0..4 { |
|||
if allowed[running][i] { |
|||
n_assignable_to[i] -= 1; |
|||
} |
|||
} |
|||
sizes[j] -= 1; |
|||
n_to_assign -= 1; |
|||
break; |
|||
} else { |
|||
n -= 1; |
|||
} |
|||
} |
|||
seen.set(running, true); |
|||
} |
|||
hand |
|||
} |
|||
|
|||
fn search(node: &mut Node) -> (Vec<usize>, &mut Node) { |
|||
let mut cur: &mut Node = node; |
|||
let mut seen: Vec<usize> = vec![]; |
|||
loop { |
|||
cur.visited += 1; |
|||
if cur.state.played.count_ones() == 52 { |
|||
// abort
|
|||
return (seen, cur); |
|||
} |
|||
let mut score: f64 = -10000.0; |
|||
let mut idx: usize = 100000; |
|||
let hands = hands(&cur.state); |
|||
let mut outin: [bool; 4] = [true; 4]; |
|||
for i in hands[usize::from(cur.state.player)].iter() { |
|||
outin[usize::from(i / 13)] = false; |
|||
} |
|||
for i in hands[usize::from(cur.state.player)].iter() { |
|||
match cur.state.trick[0] { |
|||
None => (), |
|||
Some(x) => { |
|||
if !(fromSuit(suitOf(x)) == i / 13) |
|||
&& !(outin[usize::from(fromSuit(suitOf(x)))]) |
|||
{ |
|||
continue; |
|||
} |
|||
} |
|||
} |
|||
match &cur.outcomes[usize::from(*i)] { |
|||
None => { |
|||
seen.push((*i).into()); |
|||
return (seen, cur); |
|||
} |
|||
Some(x) => { |
|||
let ourscore: f64 = (x.value as f64 / (x.visited as f64 + 1.0)) |
|||
+ 6.0 * ((cur.visited as f64).ln() / (x.visited as f64 + 1.0)).sqrt(); |
|||
if ourscore > score { |
|||
idx = (*i).into(); |
|||
score = ourscore; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
match &mut cur.outcomes[idx] { |
|||
None => panic!("This will not happen. SEARCH PANIC KW!#"), |
|||
Some(x) => { |
|||
seen.push(idx); |
|||
cur = x; |
|||
} |
|||
} |
|||
//cur.outcomes[idx].clone().unwrap();
|
|||
//cur = *(cur.outcomes[idx].unwrap());
|
|||
} |
|||
} |
|||
|
|||
fn propagate(node: &mut Node, path: Vec<usize>, value: i8) { |
|||
let mut cur: &mut Node = node; |
|||
let mut sgn = -1; |
|||
for i in path { |
|||
cur.value += sgn * i64::from(value); |
|||
sgn = -sgn; |
|||
match &mut cur.outcomes[i] { |
|||
None => panic!("This will not happen. SEARCH PANIC KQ!#"), |
|||
Some(x) => { |
|||
cur = x; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
fn simulate(state: &GameState) -> i8 { |
|||
let mut cur: GameState = state.clone(); |
|||
loop { |
|||
let tricks0: u8 = cur.tricksWonBy0; |
|||
if cur.played.count_ones() == 52 { |
|||
if cur.handPlayer == 0 || cur.handPlayer == 2 { |
|||
return max(tricks0 as i8 - 6, 0) - max(7 - tricks0 as i8, 0); |
|||
} else { |
|||
return max(7 - tricks0 as i8, 0) - max(tricks0 as i8 - 6, 0); |
|||
} |
|||
} |
|||
let card: Card; |
|||
// ok. find action
|
|||
let hands = hands(&cur); |
|||
|
|||
let mut outin: [u8; 4] = [0; 4]; |
|||
for i in hands[usize::from(cur.player)].iter() { |
|||
outin[usize::from(i / 13)] += 1; |
|||
} |
|||
// i fucked up my life on this one
|
|||
let mut n: u8 = cur.sizes[usize::from(cur.player)]; |
|||
let mut follow: bool = false; |
|||
let mut led: u8 = 100; |
|||
match cur.trick[0] { |
|||
None => (), |
|||
Some(x) => { |
|||
if outin[usize::from(fromSuit(suitOf(x)))] > 0 { |
|||
n = outin[usize::from(fromSuit(suitOf(x)))]; |
|||
led = fromSuit(suitOf(x)); |
|||
follow = true; |
|||
} |
|||
} |
|||
} |
|||
let mut p: Card = Card { |
|||
suit: Suit::Diamonds, |
|||
rank: 105, |
|||
}; // see this, something has gone terribly wrong.
|
|||
for i in hands[usize::from(cur.player)].iter() { |
|||
if follow && led != i / 13 { |
|||
continue; |
|||
} |
|||
if random::<u8>() % n == 0 { |
|||
p = numToCard(*i); |
|||
break; |
|||
} else { |
|||
n -= 1; |
|||
} |
|||
} |
|||
|
|||
card = p; |
|||
cur = state_transit(&cur, card); |
|||
} |
|||
} |
|||
|
|||
fn main() { |
|||
let mut played = bitarr!(0; 52); |
|||
played.set(12, true); |
|||
played.set(1, true); |
|||
played.set(0, true); |
|||
let mut node = new_node(GameState { |
|||
player: 0, |
|||
handPlayer: 0, |
|||
trump: Some(Suit::Hearts), |
|||
hand: [ |
|||
Some(card(Suit::Hearts, 0)), |
|||
Some(card(Suit::Hearts, 2)), |
|||
Some(card(Suit::Hearts, 7)), |
|||
Some(card(Suit::Hearts, 8)), |
|||
Some(card(Suit::Hearts, 9)), |
|||
Some(card(Suit::Hearts, 10)), |
|||
Some(card(Suit::Spades, 2)), |
|||
Some(card(Suit::Spades, 7)), |
|||
Some(card(Suit::Spades, 10)), |
|||
Some(card(Suit::Diamonds, 2)), |
|||
Some(card(Suit::Clubs, 1)), |
|||
Some(card(Suit::Clubs, 3)), |
|||
Some(card(Suit::Clubs, 7)), |
|||
], |
|||
ruffs: bitarr!(0; 16), |
|||
played, |
|||
trick: [ |
|||
Some(card(Suit::Spades, 12)), |
|||
Some(card(Suit::Spades, 1)), |
|||
Some(card(Suit::Spades, 0)), |
|||
], |
|||
tricksWonBy0: 0, |
|||
sizes: [13, 12, 12, 12], |
|||
}); |
|||
loop { |
|||
if node.state.player != node.state.handPlayer { |
|||
println!("Input the card played by player {}", node.state.player); |
|||
let mut n = String::new(); |
|||
io::stdin().read_line(&mut n).expect("Input!"); |
|||
let card: usize = n.trim().parse().expect("RIGHT!"); |
|||
match &node.outcomes[card] { |
|||
None => { |
|||
node = new_node(state_transit(&node.state, numToCard(card as u8))); |
|||
} |
|||
Some(x) => { |
|||
node = (**x).clone(); |
|||
} |
|||
} |
|||
continue; |
|||
} |
|||
for _ in 1..10000 { |
|||
let pathcur: (Vec<usize>, &mut Node) = search(&mut node); //search!
|
|||
let path = pathcur.0; |
|||
let cur = pathcur.1; |
|||
let n: i8; |
|||
if cur.state.played.count_ones() == 52 { |
|||
let tricks0: u8 = cur.state.tricksWonBy0; |
|||
n = if cur.state.handPlayer == 0 || cur.state.handPlayer == 2 { |
|||
max(tricks0 as i8 - 6, 0) - max(7 - tricks0 as i8, 0) |
|||
} else { |
|||
max(7 - tricks0 as i8, 0) - max(tricks0 as i8 - 6, 0) |
|||
} |
|||
} else { |
|||
cur.outcomes[path[path.len() - 1]] = Some(Box::new(new_node(state_transit( |
|||
&cur.state, |
|||
numToCard(path[path.len() - 1] as u8), |
|||
)))); //expand!
|
|||
n = simulate(&(*cur.outcomes[path[path.len() - 1]].as_ref().unwrap()).state); |
|||
//simulate!
|
|||
} |
|||
propagate(&mut node, path, n); |
|||
} |
|||
let mut highest_value = -4000.0; |
|||
let mut best_move = 100; |
|||
for i in 0..52 { |
|||
match &node.outcomes[i] { |
|||
None => (), |
|||
Some(x) => { |
|||
if x.value as f64 / x.visited as f64 > highest_value { |
|||
highest_value = x.value as f64 / x.visited as f64; |
|||
best_move = i; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
println!("{:?}", numToCard(best_move as u8)); |
|||
break; |
|||
match &node.outcomes[best_move] { |
|||
None => panic!("ok"), |
|||
Some(x) => { |
|||
node = (**x).clone(); |
|||
} |
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue