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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//! Go is a board game.
//! While it is usually played on a rectangular board
//! with a very specific structure,
//! it can theoretically be played on a wide variety of (undirected) graphs.
//! This module provides a suitable interface
//! which allows the rules to be evaluated
//! without knowledge of the exact board layout.

use self::indexer::Indexer;
use core::util::bool_mat::BoolMat;
use core::util::indexer;

// TODO: We might be able to get rid of a few of those lifetimes.

/// This trait captures the information needed to play Go on a board.
pub trait Board: Eq {
	/// The interface for addressing the intersection point.
	/// In classical Go, this will in some way implement a square.
	type I: Indexer;

	/// The adjacency matrix of the underlying graph.
	/// This should be symmetric.
	/// (Asymmetric versions of Go are thinkable
	/// but not currently supported by Gorrosion.)
	fn adjacencies(&self) -> &BoolMat<Self::I, Self::I>;

	/// This will soon go away, presumably.
	/// However, the boards will need to provide some way
	/// to know where to place a fixed handicap.
	/// This will come in one of the refactors of the future.
	fn is_hoshi(&self, i: <Self::I as Indexer>::Index) -> bool;
}

/// The most generic board: A graph.
/// All other boards could theoretically be implemented
/// using this as a basis.
#[derive(PartialEq, Eq, Debug)]
pub struct Graph<I: Indexer> {
	adj: BoolMat<I, I>,
}

impl<I> Board for Graph<I>
where
	I: Indexer,
{
	type I = I;

	fn adjacencies(&self) -> &BoolMat<I, I> {
		&self.adj
	}

	fn is_hoshi(&self, _i: I::Index) -> bool {
		false
	}
}

/// Rectangular boards with the classical line pattern.
/// ```none
/// ┼─┼─┼─┼
/// ┼─┼─┼─┼
/// ┼─┼─┼─┼
/// ```
#[derive(PartialEq, Eq, Debug)]
pub struct Rect {
	graph: Graph<indexer::Rect>,
}

impl Rect {
	/// Create a new rectangular board with the given measurements.
	///
	/// # Examples
	///
	/// ```
	/// # use gorrosion::core::board::Rect;
	/// # use gorrosion::core::board::Board;
	/// let rect = Rect::new(6, 4);
	/// // Get the adjacency matrix.
	/// let adj = rect.adjacencies();
	/// // Every intersection point can be reached from every intersection point
	/// // in at most (6-1) + (4-1) = 8 steps.
	/// let two_steps = adj * adj;
	/// let four_steps = &two_steps * &two_steps;
	/// // So, this should be the all-true matrix
	/// let eight_steps = &four_steps * &four_steps;
	/// assert_eq!(eight_steps[((0, 0), (5, 3))], true);
	/// ```
	pub fn new(height: usize, width: usize) -> Rect {
		let indexer = indexer::Rect::new(height, width);
		let mut adj = BoolMat::id_matrix(indexer);
		{
			let mut sym_set = |a, b| {
				adj[(a, b)] = true;
				adj[(b, a)] = true;
			};
			for j in 0..height {
				for k in 1..width {
					let a = (j, k - 1);
					let b = (j, k);
					sym_set(a, b);
				}
			}
			for j in 1..height {
				for k in 0..width {
					let a = (j - 1, k);
					let b = (j, k);
					sym_set(a, b);
				}
			}
		}
		let graph = Graph { adj };
		Rect { graph }
	}
}

impl Board for Rect {
	type I = indexer::Rect;

	fn adjacencies(&self) -> &BoolMat<Self::I, Self::I> {
		self.graph.adjacencies()
	}

	fn is_hoshi(&self, _i: <Self::I as Indexer>::Index) -> bool {
		false
	}
}

#[derive(PartialEq, Eq, Debug)]
pub struct Square {
	rect: Rect,
}

impl Square {
	pub fn new(length: usize) -> Square {
		let rect = Rect::new(length, length);
		Square { rect }
	}
}

impl Board for Square {
	type I = indexer::Rect;

	fn adjacencies(&self) -> &BoolMat<Self::I, Self::I> {
		&self.rect.adjacencies()
	}

	fn is_hoshi(&self, _i: <Self::I as Indexer>::Index) -> bool {
		false
	}
}

// TODO: Implement the three standard boards with their hoshi
// TODO: Boards need to provide more information about their hoshi
//       than whether a given intersection point is one.

// TODO: Throw out as soon as coverage tools can handle doctests.
#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn doctests() {
		let rect = Rect::new(6, 4);
		// Get the adjacency matrix.
		let adj = rect.adjacencies();
		// Every intersection point can be reached from every intersection point
		// in at most (6-1) + (4-1) = 8 steps.
		let two_steps = adj * adj;
		let four_steps = &two_steps * &two_steps;
		// So, this should be the all-true matrix
		let eight_steps = &four_steps * &four_steps;
		assert_eq!(eight_steps[((0, 0), (5, 3))], true);
	}
}