Skip to content

Instantly share code, notes, and snippets.

@shssoichiro
Last active November 28, 2015 06:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shssoichiro/23fd44be4133d0cbbd12 to your computer and use it in GitHub Desktop.
Save shssoichiro/23fd44be4133d0cbbd12 to your computer and use it in GitHub Desktop.
242 Hard
extern crate rand;
use std::cmp;
use std::env;
use std::fmt;
use rand::{thread_rng, Rng};
#[derive(PartialEq,Clone)]
enum Colors {
Black,
Yellow,
Red,
Purple
}
static COLORS: [Colors; 4] = [Colors::Black, Colors::Yellow, Colors::Red, Colors::Purple];
#[derive(Clone)]
struct Tile {
color: Colors,
number: u8
}
impl fmt::Display for Tile {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let color_tag = match self.color {
Colors::Black => "B",
Colors::Yellow => "Y",
Colors::Red => "R",
Colors::Purple => "P"
};
return write!(f, "{}{}", color_tag, self.number);
}
}
impl cmp::PartialEq for Tile {
fn eq(&self, other: &Self) -> bool {
return self.number == other.number && self.color == other.color;
}
fn ne(&self, other: &Self) -> bool {
return !self.eq(other);
}
}
fn main() {
// Build tilebag
let mut tilebag = vec![];
for _ in 0..2 {
for i in 1..14 {
for c in COLORS.iter().cloned() {
tilebag.push(Tile {
color: c,
number: i
});
}
}
}
//Get our starting hand from the command line
let mut my_tiles = vec![];
let args: Vec<String> = env::args().skip(1).collect();
assert!(args.len() == 14, "Must receive 14 starting tiles as input");
for arg in args.iter() {
let parts = arg.split_at(1);
let color = match parts.0 {
"B" => Ok(Colors::Black),
"Y" => Ok(Colors::Yellow),
"R" => Ok(Colors::Red),
"P" => Ok(Colors::Purple),
_ => Err("Bad color input")
}.unwrap();
// Pull any tiles we're given out of the tilebag
let tile = tilebag.iter().position(|i| i.color == color && i.number == parts.1.parse::<u8>().unwrap()).unwrap();
my_tiles.push(tilebag.remove(tile));
}
// See if we have any sets
let mut valid_set;
let mut rng = thread_rng();
while {
valid_set = find_sets(&mut my_tiles);
valid_set.is_none()
} {
// Draw random tiles until we find a valid set
let tile = rng.gen_range(0, tilebag.len());
let tile = tilebag.remove(tile);
println!("Drawing: {}", tile);
my_tiles.push(tile);
}
println!("Found a valid set:");
for i in valid_set.unwrap() {
print!("{} ", i);
}
println!(""); // Truncate our line of output
}
fn find_sets(hand: &mut Vec<Tile>) -> Option<Vec<Tile>> {
#[derive(Clone)]
struct TileSet {
tiles: Vec<Tile>,
sum: u16
}
// We sort once because we'll be needing it sorted for every search we do
hand.sort_by(|a, b| a.number.cmp(&b.number));
// Search for possible runs
let mut possible_runs: Vec<TileSet> = vec![];
let mut current_set: TileSet = TileSet {
tiles: vec![],
sum: 0
};
for c in COLORS.iter().cloned() {
let mut temp_hand = hand.clone();
temp_hand.retain(|i| i.color == c);
temp_hand.dedup();
let mut last: Option<Tile> = None;
for i in temp_hand {
if last.is_some() && last.clone().unwrap().number + 1 == i.number {
current_set.tiles.push(i.clone());
current_set.sum += i.number as u16;
if current_set.tiles.len() >= 3 {
possible_runs.push(current_set.clone());
}
} else {
current_set = TileSet {
tiles: vec![i.clone()],
sum: i.number as u16
};
}
last = Some(i.clone());
}
}
// Exit early if we have a run with 4 or more tiles in it
let mut best_count = 0;
let mut best_index = None;
for (index, i) in possible_runs.iter().enumerate() {
if i.tiles.len() >= 4 && i.sum >= 30 && i.tiles.len() > best_count {
best_count = i.tiles.len();
best_index = Some(index);
}
}
if best_index.is_some() {
return Some(possible_runs[best_index.unwrap()].tiles.clone());
}
// Search for possible groups
let mut possible_groups: Vec<TileSet> = vec![];
let mut last_number = 0;
let temp_hand = hand.clone();
for i in temp_hand {
if i.number != last_number {
if current_set.tiles.len() >= 3 {
possible_groups.push(current_set.clone());
}
last_number = i.number;
current_set = TileSet {
tiles: vec![i.clone()],
sum: i.number as u16
};
} else if !current_set.tiles.contains(&i) {
current_set.tiles.push(i.clone());
current_set.sum += i.number as u16;
}
}
// Let's see if any of our groups are usable on their own
best_index = None;
for (index, i) in possible_groups.iter().enumerate() {
if i.tiles.len() >= 3 && i.sum >= 30 {
best_index = Some(index);
if i.tiles.len() == 4 {
break;
}
}
}
if best_index.is_some() {
return Some(possible_groups[best_index.unwrap()].tiles.clone());
}
// Let's see if any of our runs of length 3 are usable
best_count = 0;
best_index = None;
for (index, i) in possible_runs.iter().enumerate() {
if i.tiles.len() >= 3 && i.sum >= 30 {
best_count = i.tiles.len();
best_index = Some(index);
break;
}
}
if best_count >= 3 {
return Some(possible_runs[best_index.unwrap()].tiles.clone());
}
// Now we'll look for combinations. Last resort because this is slow.
best_count = 0;
let mut best_run = None;
let mut best_group = None;
for (group, i) in possible_groups.iter().enumerate() {
for (run, j) in possible_runs.iter().enumerate() {
let mut duplicate = false;
for tile in i.tiles.iter().cloned() {
if j.tiles.contains(&tile) {
duplicate = true;
break;
}
}
if duplicate {
continue;
}
if i.tiles.len() >= 3 && j.tiles.len() >= 3 && i.sum + j.sum >= 30 {
if best_count >= i.tiles.len() + j.tiles.len() {
continue;
}
best_count = i.tiles.len() + j.tiles.len();
best_run = Some(run);
best_group = Some(group);
}
}
}
if best_run.is_some() && best_group.is_some() {
let mut result = possible_runs[best_run.unwrap()].tiles.clone();
result.append(&mut possible_groups[best_group.unwrap()].tiles);
return Some(result);
}
return None;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment