1 changed files with 0 additions and 514 deletions
@ -1,514 +0,0 @@ |
|||||
#![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