Browse Source

Initial

master
Ethan du Toit 4 years ago
commit
5290b281bb
  1. 120
      Cargo.lock
  2. 14
      Cargo.toml
  3. 514
      src/bak_kinda.src
  4. 537
      src/main.rs
  5. 514
      src/main.rs~

120
Cargo.lock

@ -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"

14
Cargo.toml

@ -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

514
src/bak_kinda.src

@ -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();
}
}
}
}

537
src/main.rs

@ -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();
}
}
}
}

514
src/main.rs~

@ -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…
Cancel
Save