From e5c655eafb8ee636e02ee99286f7ef8a0f5b4b52 Mon Sep 17 00:00:00 2001 From: Artemis Tosini Date: Wed, 29 Jan 2020 17:14:00 +0000 Subject: [PATCH] add scripts, the start of a monte-carlo --- .gitignore | 4 + montecarlo/rummicube/.gitignore | 1 + montecarlo/rummicube/Cargo.lock | 6 + montecarlo/rummicube/Cargo.toml | 7 + montecarlo/rummicube/src/iteration.rs | 2 + montecarlo/rummicube/src/main.rs | 13 ++ montecarlo/rummicube/src/player.rs | 0 montecarlo/rummicube/src/tests.rs | 35 +++++ montecarlo/rummicube/src/tile.rs | 179 ++++++++++++++++++++++++++ scripts/ykclip-menu.sh | 9 ++ 10 files changed, 256 insertions(+) create mode 100644 .gitignore create mode 100644 montecarlo/rummicube/.gitignore create mode 100644 montecarlo/rummicube/Cargo.lock create mode 100644 montecarlo/rummicube/Cargo.toml create mode 100644 montecarlo/rummicube/src/iteration.rs create mode 100644 montecarlo/rummicube/src/main.rs create mode 100644 montecarlo/rummicube/src/player.rs create mode 100644 montecarlo/rummicube/src/tests.rs create mode 100644 montecarlo/rummicube/src/tile.rs create mode 100755 scripts/ykclip-menu.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..12b7eb0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.py[co] +__pycache__/ +*~ diff --git a/montecarlo/rummicube/.gitignore b/montecarlo/rummicube/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/montecarlo/rummicube/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/montecarlo/rummicube/Cargo.lock b/montecarlo/rummicube/Cargo.lock new file mode 100644 index 0000000..fa00b0e --- /dev/null +++ b/montecarlo/rummicube/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "rummicube" +version = "0.1.0" + diff --git a/montecarlo/rummicube/Cargo.toml b/montecarlo/rummicube/Cargo.toml new file mode 100644 index 0000000..cfb6983 --- /dev/null +++ b/montecarlo/rummicube/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rummicube" +version = "0.1.0" +authors = ["Artemis Tosini "] +edition = "2018" + +[dependencies] diff --git a/montecarlo/rummicube/src/iteration.rs b/montecarlo/rummicube/src/iteration.rs new file mode 100644 index 0000000..33927f0 --- /dev/null +++ b/montecarlo/rummicube/src/iteration.rs @@ -0,0 +1,2 @@ +use crate::tile::*; + diff --git a/montecarlo/rummicube/src/main.rs b/montecarlo/rummicube/src/main.rs new file mode 100644 index 0000000..dc0f650 --- /dev/null +++ b/montecarlo/rummicube/src/main.rs @@ -0,0 +1,13 @@ +#![allow(unused_imports, unused_variables, dead_code)] + +mod player; +mod tile; +mod iteration; + +#[cfg(test)] +mod tests; + +use crate::player::*; +use crate::tile::*; + +fn main() {} diff --git a/montecarlo/rummicube/src/player.rs b/montecarlo/rummicube/src/player.rs new file mode 100644 index 0000000..e69de29 diff --git a/montecarlo/rummicube/src/tests.rs b/montecarlo/rummicube/src/tests.rs new file mode 100644 index 0000000..43395b3 --- /dev/null +++ b/montecarlo/rummicube/src/tests.rs @@ -0,0 +1,35 @@ +use crate::tile::*; + +#[test] +fn test_trivial_run() { + let tiles: Vec = (1..=MIN_GROUP_SIZE) + .map(|i| NormalTile::new(TileColor::Blue, i as u32)) + .collect(); + + let groups = find_groups(&tiles.iter().map(|n| Tile::Normal(*n)).collect()); + let expected = vec![Group::new(GroupType::Run, tiles)]; + assert_eq!(groups, expected) +} + +#[test] +fn test_5_run() { + let tiles: Vec = (1..=5) + .map(|i| NormalTile::new(TileColor::Blue, i as u32)) + .collect(); + + let groups = find_groups(&tiles.iter().map(|n| Tile::Normal(*n)).collect()); + let expected = vec![Group::new(GroupType::Run, tiles)]; + assert_eq!(groups, expected) + +} + +#[test] +fn test_too_short_run() { + let tiles: Vec = (1..=2) + .map(|i| NormalTile::new(TileColor::Blue, i as u32)) + .collect(); + + let groups = find_groups(&tiles.iter().map(|n| Tile::Normal(*n)).collect()); + let expected = vec![]; + assert_eq!(groups, expected) +} diff --git a/montecarlo/rummicube/src/tile.rs b/montecarlo/rummicube/src/tile.rs new file mode 100644 index 0000000..86f080e --- /dev/null +++ b/montecarlo/rummicube/src/tile.rs @@ -0,0 +1,179 @@ +use std::iter::FromIterator; + +pub(crate) const MAX_TILE: u32 = 13; +pub(crate) const NUM_WILDCARDS: u32 = 2; +pub(crate) const NUM_COPIES: u32 = 2; +pub(crate) const MIN_GROUP_SIZE: u32 = 3; +pub(crate) const COLORS: [TileColor; 4] = [ + TileColor::Blue, + TileColor::Black, + TileColor::Red, + TileColor::Orange, +]; + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub(crate) enum TileColor { + Blue, + Black, + Red, + Orange, +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub(crate) struct NormalTile { + color: TileColor, + value: u32, +} + +impl NormalTile { + pub(crate) fn new(color: TileColor, value: u32) -> NormalTile { + NormalTile { color, value } + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub(crate) enum Tile { + Normal(NormalTile), + Wildcard, +} + +impl From for Option { + fn from(t: Tile) -> Self { + match t { + Tile::Normal(n) => Some(n), + Tile::Wildcard => None, + } + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub(crate) enum GroupType { + Run, + Colors, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub(crate) struct Group { + typ: GroupType, + tiles: Vec, + count: u32, + sum: u32, +} +impl Group { + pub(crate) fn new(typ: GroupType, tiles: Vec) -> Group { + Group { + typ, + tiles: tiles.clone(), + count: tiles.len() as u32, + sum: tiles.iter().map(|tile| tile.value).sum(), + } + } +} +pub(crate) fn all_tiles() -> Vec { + let mut tiles = COLORS + .iter() + .flat_map(|color| build_run(color.clone(), (1..(MAX_TILE + 1)).collect())) + .map(|normal_tile| Tile::Normal(normal_tile)) + .collect::>(); + + let orig_tiles = tiles.clone(); + + for _ in 1..NUM_COPIES { + tiles.append(&mut orig_tiles.clone()); + } + + tiles.append( + &mut std::iter::repeat(Tile::Wildcard) + .take(NUM_WILDCARDS as usize) + .collect(), + ); + + tiles +} + +fn build_run(color: TileColor, values: Vec) -> Vec { + values + .iter() + .map(|value| NormalTile { + color, + value: *value, + }) + .collect() +} + +fn remove_instance(vec: &mut Vec, item: I) { + if let Some(idx) = vec.iter().position(|it| *it == item) { + vec.remove(idx); + } +} + +fn remove_tiles(tiles: &mut Vec, run: Vec) { + for tile in run { + remove_instance(tiles, tile); + } +} + +pub(crate) fn find_groups(tiles: &Vec) -> Vec { + // Note that this is a greedy algorithm, so it is non-optimal. + // Hopefully the difference isn't too big + + let mut groups = Vec::new(); + let mut tiles_left: Vec = tiles + .iter() + .filter_map(|tile| Option::from(*tile)) + .collect(); + + for color in &COLORS { + let mut tile_numbers: Vec<_> = tiles_left + .iter() + .filter_map(|tile| { + if tile.color == *color { + Some(tile.value) + } else { + None + } + }) + .collect(); + + tile_numbers.sort_unstable(); + + if let Some(min_tile) = tile_numbers.first() { + let mut i = *min_tile; + let mut current_group = Vec::new(); + while i <= MAX_TILE { + if tile_numbers.contains(&i) { + current_group.push(i); + } else if current_group.len() as u32 >= MIN_GROUP_SIZE { + let run = build_run(*color, current_group.clone()); + groups.push(Group::new(GroupType::Run, run.clone())); + remove_tiles(&mut tiles_left, run.clone()); + + current_group.clear(); + } + i += 1; + } + } + } + + // Next find the groups of different colors + + for i in 1..=MAX_TILE { + let mut tiles = Vec::new(); + for color in &COLORS { + let tile = NormalTile { + color: color.clone(), + value: i, + }; + if tiles_left.contains(&tile) { + tiles.push(tile); + } + } + if tiles.len() as u32 >= MIN_GROUP_SIZE { + groups.push(Group::new(GroupType::Colors, tiles.clone())); + remove_tiles(&mut tiles_left, tiles.clone()); + tiles.clear(); + } + } + + groups.clone() +} diff --git a/scripts/ykclip-menu.sh b/scripts/ykclip-menu.sh new file mode 100755 index 0000000..928f1f9 --- /dev/null +++ b/scripts/ykclip-menu.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +name=$(ykman oath list | wofi -k /dev/null -i -d) + +line=$(ykman oath code $name | head -n 1) +if test -n "$line" +then + echo $line | awk '{print $NF}' | wl-copy +fi