Skip to content

Instantly share code, notes, and snippets.

@ExpHP
Created August 12, 2016 16: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 ExpHP/ddebf621e80bc92e7b769c2dd2172381 to your computer and use it in GitHub Desktop.
Save ExpHP/ddebf621e80bc92e7b769c2dd2172381 to your computer and use it in GitHub Desktop.
halp-2-rust
use serde;
use serde::de::Deserialize;
use serde_json as json;
use std::collections::BTreeSet;
use itertools::Itertools;
use ::util::MyIteratorExt;
use ::layers::{Layers,Layer};
use ::ruleset::traits::RuleSet;
use ::grid::Hexagonal;
use ::grid::hexagonal::{Node,Cubic,MutualNeighborhood};
#[derive(Serialize, Deserialize)]
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)]
#[derive(Debug, Clone, Copy)]
pub struct Mx2Monovacancy;
pub type State = Hexagonal<Layers>;
#[allow(unused_attributes)]
#[derive(Serialize, Deserialize, ToJson)]
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)]
#[derive(Debug, Clone, Copy)]
pub enum Move {
Form(Node,Layer),
Move(Node,Node,Layer),
Flip(Node),
Trefoil(Trefoil),
}
pub type Trefoil = (Node,Node,Node);
fn sorted3<T:Ord>(mut a:T, mut b:T, mut c:T) -> (T,T,T) {
minmax_inplace(&mut a, &mut b);
minmax_inplace(&mut a, &mut c);
minmax_inplace(&mut a, &mut b);
(a,b,c)
}
#[inline]
fn minmax_inplace<T:Ord>(a: &mut T, b: &mut T) {
if a > b { ::std::mem::swap(a, b) };
}
pub struct Event {
changes: Vec<(Node,Layer)>,
}
pub type Toggle = (Node,Layer);
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)]
#[derive(Debug, Clone, Copy)]
pub enum Kind {
Form(bool),
Flip,
Trefoil,
Move{ from: bool, to: bool, metal: bool, comb: bool },
}
static KIND_FORM_0_TO_1: &'static str = "FormFirst";
static KIND_FORM_1_TO_2: &'static str = "FormSecond";
static KIND_TREFOIL: &'static str = "MakeTrefoil";
static KIND_FLIP: &'static str = "Flip";
static KIND_MOVE_PREFIX: &'static str = "Move";
const KIND_MOVE_CHAR_PRESENT: char = 'X';
const KIND_MOVE_CHAR_VACANT: char = '_';
fn status_kind_char(vacant: bool) -> char {
if vacant { KIND_MOVE_CHAR_VACANT }
else { KIND_MOVE_CHAR_PRESENT }
}
fn kind_char_is_vacant(c: char) -> Option<bool> {
match c {
KIND_MOVE_CHAR_VACANT => Some(true),
KIND_MOVE_CHAR_PRESENT => Some(false),
_ => None,
}
}
impl serde::Serialize for Kind {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: serde::Serializer,
{
match self {
&Kind::Form(false) => serializer.serialize_str(KIND_FORM_0_TO_1),
&Kind::Form(true) => serializer.serialize_str(KIND_FORM_1_TO_2),
&Kind::Flip => serializer.serialize_str(KIND_FLIP),
&Kind::Trefoil => serializer.serialize_str(KIND_TREFOIL),
&Kind::Move{from,to,metal,comb} => {
let chars1 = KIND_MOVE_PREFIX.chars();
let chars2 = vec![from, to, metal, comb]
.into_iter().map(status_kind_char);
let s:String = chars1.chain(chars2).collect();
serializer.serialize_str(&s)
},
}
}
}
impl Deserialize for Kind {
fn deserialize<D>(deserializer: &mut D) -> Result<Kind, D::Error>
where D: serde::Deserializer,
{ deserializer.deserialize(KindVisitor) }
}
struct KindVisitor;
impl serde::de::Visitor for KindVisitor {
type Value = Kind;
fn visit_string<E>(&mut self, s: String) -> Result<Kind, E>
where E: ::serde::de::Error,
{ self.visit_str(&s) }
fn visit_str<E>(&mut self, s: &str) -> Result<Kind, E>
where E: ::serde::de::Error,
{
println!("<{}>",s);
if s == KIND_FLIP { Ok(Kind::Flip) }
else if s == KIND_TREFOIL { Ok(Kind::Trefoil) }
else if s == KIND_FORM_0_TO_1 { Ok(Kind::Form(false)) }
else if s == KIND_FORM_1_TO_2 { Ok(Kind::Form(true)) }
else if s.starts_with(KIND_MOVE_PREFIX) {
let mut cs = s[KIND_MOVE_PREFIX.len()..].chars()
.map(kind_char_is_vacant)
.map(|opt| opt.expect("bad move char")); // FIXME use Err
// FIXME use Err
let from = cs.next().expect("not enough chars in Move Kind");
let to = cs.next().expect("not enough chars in Move Kind");
let metal = cs.next().expect("not enough chars in Move Kind");
let comb = cs.next().expect("not enough chars in Move Kind");
assert!(cs.next().is_none(), "too many chars in Move Kind");
Ok(Kind::Move{from:from, to:to, metal:metal, comb:comb})
}
else { Err(serde::de::Error::custom("bad prefix for kind")) }
}
}
#[test]
fn test_kind_serialize_round_trip() {
println!("");
for kind in Mx2Monovacancy.all_kinds() {
println!("ser: <{:?}>", kind);
let s = json::to_string(&kind).unwrap_or_else(|e| panic!("{}", e));
println!(" de: <{}>", &s);
let k2 = json::from_str(&s).unwrap_or_else(|e| panic!("{}", e));
assert_eq!(kind, k2);
}
}
impl RuleSet for Mx2Monovacancy {
type State = State;
type Move = Move;
type Event = Event;
type Toggle = Toggle;
type Kind = Kind;
fn all_moves(&self, state: &State) -> Vec<(Move,Kind)> {
let mut out = vec![];
for node in state.nodes() {
for_normal_moves_from(state, node, |x| {out.push(x);});
}
out
}
fn all_kinds(&self) -> Vec<Kind> {
let mut out = Vec::new();
out.push(Kind::Form(true));
out.push(Kind::Form(false));
out.push(Kind::Flip);
out.push(Kind::Trefoil);
out.extend((0..16).map(|n|
Kind::Move {
from: n&1 == 1,
to: n&2 == 2,
metal: n&4 == 4,
comb: n&8 == 8,
}
));
out
}
fn affected_moves(&self, ev: &Event, state: &State) -> Vec<(Move,Kind)> {
let &Event { ref changes } = ev;
// Nodes up to one tile away
let mut nodes = changes.iter().map(|&(node,_)| node).collect(): BTreeSet<_>;
let clone = nodes.clone();
nodes.extend(clone.into_iter().flat_map(|n| state.neighbors(n)));
let mut out = vec![];
for node in nodes.iter().cloned() {
for_normal_moves_from(state, node, |x| {out.push(x);});
}
for_trefoil_moves_involving(state, nodes, |x| {out.push(x);});
debug_assert_eq!(out.len(), out.iter().cloned().map(|(m,_)| m).unique().count());
out
}
fn move_event(&self, mv: Move, _state: &State) -> Event {
let changes = match mv {
Move::Form(node,layer) => vec![(node,layer)],
Move::Flip(node) => vec![(node,1), (node,2)],
Move::Move(old,new,layer) => vec![(old,layer), (new,layer)],
Move::Trefoil((a,b,c)) => vec![(a,1),(a,2),(b,1),(b,2),(c,1),(c,2)],
};
Event { changes: changes }
}
fn perform(&self, ev: &Event, state: &mut State) {
let &Event { ref changes } = ev;
for (node,layer) in changes.iter().cloned() {
let layers = state[node].invert_layer(layer);
state[node] = layers;
}
}
fn event_toggles(&self, ev: &Event) -> Vec<Toggle> {
let &Event { ref changes } = ev;
changes.clone()
}
fn all_toggles(&self, state: &State) -> Vec<Toggle> {
state.iter_with_nodes()
.flat_map(|(node,layers)| layers.into_iter().map(move |x| (node,x)))
.collect()
}
}
pub(self) fn for_normal_moves_from<F>(state: &State, node: Node, mut cb: F)
where F: FnMut((Move,Kind)),
{
let layers = state[node];
for layer in layers.invert() {
(cb)((
Move::Form(node,layer),
Kind::Form(!layers.is_empty()),
))
}
if layers.is_single() {
(cb)((
Move::Flip(node),
Kind::Flip,
))
}
state.for_mutual_neighborhoods(node, |hood| {
let MutualNeighborhood { neighbor, pos_mutual, neg_mutual } = hood;
for layer in layers {
if state[neighbor].has_layer(layer) { continue }
let mv = Move::Move(node,neighbor,layer);
let kind = Kind::Move {
from: state[node].is_double(),
to: !state[neighbor].is_empty(),
metal: state[pos_mutual].has_layer(layer),
comb: state[neg_mutual].has_layer(layer),
};
(cb)((mv,kind))
}
})
}
/// One of the six possible displacements (in cubic coords)
/// between two nodes in a trefoil defect.
const TREFOIL_NEIGHBOR_DISP: Cubic = (2,-2,0);
fn for_trefoil_moves_involving<I,F>(state: &State, nodes: I, mut cb: F)
where F: FnMut((Move,Kind)), I: IntoIterator<Item=Node>,
{
// We seek groups of three divacancies all mutually related by
// trefoil displacements, of which at least one is in `nodes`.
// That is, for each divacancy in `nodes`...
nodes.into_iter().filter(|&x| state[x].is_double())
// ...find pairs of two divacancies related to it...
.flat_map(|node|
state.rotations_60(node, TREFOIL_NEIGHBOR_DISP)
.filter(|&x| state[x].is_double())
.combinations_n(2)
// ...which are also related to each other.
.filter(|pair| state.rotations_60(pair[0], TREFOIL_NEIGHBOR_DISP).any_eq(&pair[1]))
// Put them into a canonical form.
.map(move |pair| sorted3(node, pair[0], pair[1]))
)
// The same trefoil will have been produced multiple times if more than
// one of its nodes are in `nodes`.
.unique()
.map(|tref| (Move::Trefoil(tref), Kind::Trefoil))
.foreach(|x| (cb)(x)) // emit results
}
#[cfg(test)]
pub mod tests {
use std::collections::BTreeSet;
use std::iter::FromIterator;
use super::*;
use itertools::Itertools;
use ::grid::hexagonal::{Node,Hexagonal};
use ::layers::Layers;
use super::for_normal_moves_from;
fn moves_from_node(state: &State, node: Node) -> BTreeSet<(Move,Kind)> {
let mut out = vec![];
for_normal_moves_from(state, node, |x| {out.push(x);});
assert_eq!(out.len(), out.iter().cloned().map(|(m,_)| m).unique().count());
out.into_iter().collect()
}
#[test]
fn empty_node_moves() {
let state = Hexagonal::new_from_const((10,10), Layers::new_empty());
assert_eq!(
moves_from_node(&state, (5,5)),
BTreeSet::from_iter(vec![
(Move::Form((5,5),1), Kind::Form(false)),
(Move::Form((5,5),2), Kind::Form(false)),
])
);
}
#[test]
fn single_node_moves() {
let mut state = Hexagonal::new_from_const((10,10), Layers::new_empty());
state[(5,5)] = Layers::new_single(2);
let mv_kind = Kind::Move{ from:false, to:false, metal:false, comb:false };
assert_eq!(
moves_from_node(&state, (5,5)),
BTreeSet::from_iter(vec![
(Move::Flip, Kind::Flip),
(Move::Form((5,5)), Kind::Form(true)),
(Move::Move((5,5),(4,6),2), mv_kind), // -+
(Move::Move((5,5),(6,4),2), mv_kind), // +-
(Move::Move((5,5),(6,5),2), mv_kind), // +0
(Move::Move((5,5),(4,5),2), mv_kind), // -0
(Move::Move((5,5),(5,4),2), mv_kind), // 0-
(Move::Move((5,5),(5,6),2), mv_kind), // 0+
])
);
}
#[test]
fn move_kinds() {
let mut state = Hexagonal::new_from_const((10,10), Layers::new_empty());
state[(5,5)] = Layers::new_single(1);
let from_kind = Kind::Move{ from:true, to:false, metal:false, comb:false };
let to_kind = Kind::Move{ from:false, to:true, metal:false, comb:false };
let metal_kind = Kind::Move{ from:false, to:false, metal:true, comb:false };
let comb_kind = Kind::Move{ from:false, to:false, metal:false, comb:true };
// closures to temporarily place a vacancy somewhere and test whether it
// has a given move
let mut require_move = |from, to, layer, kind| {
state[from] = Layers::new_single(layer);
assert!(moves_from_node(&state, from)
.any_eq(&Move::Move(from,to,layer), kind));
state[from] = Layers::new_empty();
};
let mut require_no_move = |from, to, layer| {
state[from] = Layers::new_single(layer);
assert!(moves_from_node(&state, from).map(|(m,_)| m)
.all(|(m != &Move::Move(from,to,layer))));
state[from] = Layers::new_empty();
}
require_move((5,5), (4,5), 2, from_kind);
require_move((5,5), (4,5), 2, to_kind); // FIXME should fail
require_move((4,5), (5,5), 1, from_kind); // FIXME should fail
require_move((4,5), (5,5), 2, to_kind);
require_no_move((4,5), (5,5), 1);
require_no_move((4,5), (5,5), 2); // FIXME should fail
require_no_move((
assert!(moves_from_node(&state, (5,5)).contains(&(Move::Move((5,5),(4,6),2), mv_kind)));
BTreeSet::from_iter(vec![
(Move::Flip, Kind::Flip),
(Move::Form((5,5)), Kind::Form(true)),
(Move::Move((5,5),(4,6),2), mv_kind), // -+
(Move::Move((5,5),(6,4),2), mv_kind), // +-
(Move::Move((5,5),(6,5),2), mv_kind), // +0
(Move::Move((5,5),(4,5),2), mv_kind), // -0
(Move::Move((5,5),(5,4),2), mv_kind), // 0-
(Move::Move((5,5),(5,6),2), mv_kind), // 0+
])
);
}
#[test]
fn trefoil_moves() {
let mut state = Hexagonal::new_from_const((10,10), Layers::new_empty());
// FIXME many more
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment