1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use super::*;
use super::grids::*;
#[derive(PartialEq, Eq, Clone, Hash, Debug, Serialize, Deserialize)]
pub struct Snake {
pub segments: Vec<Vector>,
#[serde(skip_serializing, skip_deserializing)]
previous_tail: Option<Vector>,
}
impl Snake {
pub fn new(segments: Vec<Vector>) -> Snake {
Snake {
segments: segments,
previous_tail: None,
}
}
pub fn is_head_at(&self, v: &Vector) -> bool {
!self.segments.is_empty() && self.segments[0] == *v
}
pub fn has_segment_at(&self, v: &Vector) -> bool {
self.segments.iter().any(|x| x == v)
}
pub fn has_collided_into(&self, other: &Snake) -> bool {
let my_head = self.segments[0];
let mut next_candidate = my_head.distance(&other.segments[0]);
while let Some(here) = other.segments.get(next_candidate) {
if my_head == *here {
return true;
}
next_candidate += my_head.distance(here);
}
false
}
pub fn step_in_direction(&mut self, dir: Direction) {
self.previous_tail = self.segments.last().cloned();
if !self.segments.is_empty() {
for i in (1..self.segments.len()).rev() {
self.segments[i] = self.segments[i - 1];
}
self.segments[0] = self.segments[0].neighbour(&dir);
}
}
pub fn grow(&mut self) {
if let Some(previous_tail) = self.previous_tail {
self.segments.push(previous_tail);
self.previous_tail = None;
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CauseOfDeath {
NoMoveMade,
CollidedWithSnake,
CollidedWithBounds,
}
#[cfg(test)]
mod tests {
use quickcheck::{Arbitrary, Gen, quickcheck};
use super::*;
impl Arbitrary for Snake {
fn arbitrary<G: Gen>(g: &mut G) -> Snake {
let size = {
let s = g.size();
g.gen_range(0, s)
};
let head: Vector = Arbitrary::arbitrary(g);
let segments = (0..size)
.scan(head, |state, _| {
let dir = Arbitrary::arbitrary(g);
*state = (*state).neighbour(&dir);
Some(*state)
})
.collect();
return Snake {
segments: segments,
previous_tail: None,
};
}
fn shrink(&self) -> Box<Iterator<Item = Snake>> {
let mut shrinks = Vec::new();
for i in 0..self.segments.len() {
shrinks.push(Snake {
segments: self.segments[..i].to_vec(),
previous_tail: None,
})
}
return Box::new(shrinks.into_iter());
}
}
fn snake_is_connected_prop(snake: Snake) -> bool {
snake
.segments
.windows(2)
.all(|x| x[0].distance(&x[1]) == 1)
}
#[test]
fn snake_is_connected() {
quickcheck(snake_is_connected_prop as fn(Snake) -> bool);
}
fn step_preserves_connectedness_prop(snake: Snake, dir: Direction) -> bool {
let mut snake = snake.clone();
snake.step_in_direction(dir);
return snake_is_connected_prop(snake);
}
#[test]
fn step_preserves_connectedness() {
quickcheck(step_preserves_connectedness_prop as fn(Snake, Direction) -> bool);
}
fn head_is_at_head_prop(snake: Snake) -> bool {
snake.segments.len() == 0 || snake.is_head_at(&snake.segments[0])
}
#[test]
fn head_is_at_head() {
quickcheck(head_is_at_head_prop as fn(Snake) -> bool);
}
fn only_head_is_at_head_prop(snake: Snake) -> bool {
if snake.segments.len() == 0 {
return true;
}
let head_position = snake.segments[0];
for x in snake.segments.clone() {
if x != head_position && snake.is_head_at(&x) {
return false;
}
}
return true;
}
#[test]
fn only_head_is_at_head() {
quickcheck(only_head_is_at_head_prop as fn(Snake) -> bool);
}
}