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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
use petgraph::graph::NodeIndex;
use std::clone::Clone;
use std::default::Default;
use std::fmt;
use std::fmt::{Debug, Display};
use std::sync::{Arc, RwLock};

/// Standard [`Result`](https://doc.rust-lang.org/std/result/) type alias for the library. Error type is [`TournamentError`](enum.TournamentError.html)
pub type Result<T> = std::result::Result<T, TournamentError>;

/// Implement this trait to create a system for solving battles betweeen two structs.
///
/// # Example
/// The larger number wins. Ties are resolved randomly.
/// ```
/// use crate::MyMetadata;
///
/// #[derive(Clone)]
/// struct U32BattleSystem;
/// impl BattleSystem<u32, MyMetadata> for U32BattleSystem {
///
/// 	fn battle(
/// 		a_arc: Arc<RwLock<u32>>,
/// 		b_arc: Arc<RwLock<u32>>,
/// 	) -> BattleResult<MyMetadata> {
/// 		use TournamentRoundResult::*;
/// 		let a = a_arc.read().unwrap();
/// 		let b = b_arc.read().unwrap();
///
/// 		if *a > *b {
/// 			BattleResult::Solved(A, MyMetadata::new())
/// 		} else if *a < *b {
/// 			BattleResult::Solved(B, MyMetadata::new())
/// 		} else {
/// 			BattleResult::Tie
/// 		}
/// 	}
///
/// 	fn tiebreaker(
/// 		_: Arc<RwLock<u32>>,
/// 		_: Arc<RwLock<u32>>,
/// 	) -> (TournamentRoundResult, MyMetadata) {
/// 		use rand::prelude::*;
/// 		use TournamentRoundResult::*;
/// 		(
/// 			if random::<f32>() > 0.5 { A } else { B },
/// 			MyMetadata::new()
/// 		)
/// 	}
///
/// }
/// ```
pub trait BattleSystem<
	E: Debug + Display + Clone,
	M: Debug + Display + Clone + Default,
>: Clone
{
	/// - Resolves a round played between two entrants encapsulated in [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)`<`[`RwLock`](https://doc.rust-lang.org/std/sync/struct.RwLock.html)`<E>>`s, allowing for mutation of entrants between rounds.
	///
	/// - Example funcationality: reduce a fighter's HP during a round, and retain the change in later rounds.
	fn battle(a: Arc<RwLock<E>>, b: Arc<RwLock<E>>) -> BattleResult<M>;

	/// - In case `battle` returns a [`BattleResult::Tie`](enum.BattleResult.html#variant.Tie), run a tiebreaker that must return a successful result.
	fn tiebreaker(
		a: Arc<RwLock<E>>,
		b: Arc<RwLock<E>>,
	) -> (TournamentRoundResult, M);
}

/// Returned by the [`battle()`](trait.BattleSystem.html#tymethod.battle) function in implementations of [`BattleSystem`](trait.BattleSystem.html)
pub enum BattleResult<M: Debug + Display + Clone + Default> {
	/// A successful solve, returns whether [`A`](enum.TournamentRoundResult.html#variant.A) or [`B`](enum.TournamentRoundResult.html#variant.A) wins, along with a piece of round metadata of type `M`.
	Solved(TournamentRoundResult, M),
	/// A solve that resulted in a tie. When [`battle()`](trait.BattleSystem.html#tymethod.battle) returns this, [`tiebreaker()`](trait.BattleSystem.html#tymethod.tiebreaker) is run immediately after.
	Tie,
}

/// The Id of an entrant in a [`Tournament`](struct.Tournament.html). A wrapper around a single `usize`. Implements [`Display`](https://doc.rust-lang.org/stable/rust-by-example/hello/print/print_display.html)
#[derive(Debug, Clone, Copy)]
pub struct EntrantId(pub usize);
impl fmt::Display for EntrantId {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		write!(f, "Entrant #{}", self.0)
	}
}

/// The [node weight](https://docs.rs/petgraph/0.5.1/petgraph/graph/struct.Graph.html#method.node_weight) of a [`Tournament`](struct.Tournament.html)'s internal [graph](struct.Tournament.html#method.graph).
#[derive(Debug, Clone, Copy)]
pub enum TournamentNode<M: Debug + Display + Clone + Default> {
	/// Represents the starting point of an entrant within the tournament bracket. Links to exactly one `Round` node.
	Entrant(EntrantId),
	/// Represents a round in the tournament. Links to two previous rounds or entrant nodes, and one future round node (except for the final round)
	Round(TournamentRound<M>),
}
impl<M: Debug + Display + Clone + Default> fmt::Display for TournamentNode<M> {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match self {
			Self::Entrant(eid) => write!(f, "{}", eid),
			Self::Round(r) => write!(f, "{}", r),
		}
	}
}
impl<M: Debug + Display + Clone + Default> TournamentNode<M> {
	/// Get the entrant of the node. Returns `None` if the node is a `TournamentNode::Round`
	pub fn entrant(&self) -> Option<&EntrantId> {
		match self {
			Self::Entrant(eid) => Some(eid),
			_ => None,
		}
	}
	/// Get the round of the node. Returns `None` if the node is a `TournamentNode::Entrant`
	pub fn round(&self) -> Option<&TournamentRound<M>> {
		match self {
			Self::Round(r) => Some(r),
			_ => None,
		}
	}
	/// Get the metadata of a node. Returns `None` if the node is a `TournamentNode::Entrant`, or is incomplete.
	pub fn metadata(&self) -> Option<&M> {
		if let Self::Round(round) = self {
			round.metadata()
		} else {
			None
		}
	}
	/// Get a mutable reference to the metadata of a node. Returns `None` if the node is a `TournamentNode::Entrant`, or is incomplete.
	pub fn metadata_mut(&mut self) -> Option<&mut M> {
		if let Self::Round(round) = self {
			round.metadata_mut()
		} else {
			None
		}
	}
	/// Get the result of a node. Returns `None` if the node is a `TournamentNode::Entrant`, or is incomplete.
	pub fn result(&self) -> Option<&TournamentRoundResult> {
		if let Self::Round(round) = self {
			round.result()
		} else {
			None
		}
	}
}

/// A single round in a [`Tournament`](struct.Tournament.html)'s bracket.
#[derive(Debug, Clone, Copy)]
pub enum TournamentRound<M: Debug + Display + Clone + Default> {
	/// Represents a round that hasn't be solved / played out yet.
	Incomplete,
	/// Represents a round that's been solved, and has a winner.
	Complete {
		/// The winner of the round.
		result: TournamentRoundResult,
		/// Metadata associated with this round, as returned from [`BattleSystem::battle`](trait.BattleSystem.html#tymethod.battle) or [`BattleSystem::tiebreaker`](trait.BattleSystem.html#tymethod.tiebreaker)
		metadata: M,
	},
}
impl<M: Debug + Display + Clone + Default> fmt::Display for TournamentRound<M> {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match self {
			Self::Incomplete => write!(f, "Incomplete"),
			Self::Complete { result, metadata } => {
				write!(f, "{} --- {}", result, metadata)
			}
		}
	}
}
impl<M: Debug + Display + Clone + Default> TournamentRound<M> {
	/// Get the metadata of a round. Returns `None` if the round is incomplete.
	pub fn metadata(&self) -> Option<&M> {
		if let TournamentRound::<M>::Complete {
			result: _,
			metadata,
		} = self
		{
			Some(&metadata)
		} else {
			None
		}
	}
	/// Get a mutable reference to the metadata of a round. Returns `None` if the round is incomplete.
	pub fn metadata_mut(&mut self) -> Option<&mut M> {
		if let TournamentRound::<M>::Complete {
			result: _,
			metadata,
		} = self
		{
			Some(metadata)
		} else {
			None
		}
	}
	/// Get the result of a round. Returns `None` if the round is incomplete.
	pub fn result(&self) -> Option<&TournamentRoundResult> {
		if let TournamentRound::<M>::Complete {
			result,
			metadata: _,
		} = self
		{
			Some(result)
		} else {
			None
		}
	}
}

/// The [edge weight](https://docs.rs/petgraph/0.5.1/petgraph/graph/struct.Graph.html#method.edge_weight) of a [`Tournament`](struct.Tournament.html)'s internal [graph](struct.Tournament.html#method.graph).
///
/// Convertible to [`TournamentRoundResult`](enum.TournamentRoundResult.html)
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum TournamentEdge {
	/// Represents a connection from one round to the next on size `A`.
	A,
	/// Represents a connection from one round to the next on side `B`.
	B,
}
impl std::convert::From<TournamentRoundResult> for TournamentEdge {
	fn from(r: TournamentRoundResult) -> Self {
		match r {
			TournamentRoundResult::A => Self::A,
			TournamentRoundResult::B => Self::B,
		}
	}
}

/// Represents the winner of a solved [`TournamentRound`](enum.TournamentRound.html)
///
/// Convertible to [`TournamentEdge`](enum.TournamentEdge.html)
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum TournamentRoundResult {
	/// Represents the winner being on side `A`.
	A,
	/// Represents the winner being on side `B`.
	B,
}
impl std::convert::From<TournamentEdge> for TournamentRoundResult {
	fn from(e: TournamentEdge) -> Self {
		match e {
			TournamentEdge::A => Self::A,
			TournamentEdge::B => Self::B,
		}
	}
}
impl fmt::Display for TournamentRoundResult {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match self {
			Self::A => write!(f, "A wins"),
			Self::B => write!(f, "B wins"),
		}
	}
}

/// Enum used for all errors in the crate.
#[derive(Debug, Clone, Copy)]
pub enum TournamentError {
	/// Returned when a [`Tournament`](struct.Tournament.html)'s internal [graph](struct.Tournament.html#method.graph) doesn't contain a certain [`NodeIndex`](https://docs.rs/petgraph/0.5.1/petgraph/graph/struct.NodeIndex.html)
	RoundNotFound(NodeIndex),
	/// Returned when a [`Tournament`](struct.Tournament.html) doesn't contain an entrant of a certain [`EntrantId`](struct.EntrantId.html)
	EntrantNotFound(EntrantId),
	/// Returned when a [`Tournament`](struct.Tournament.html)'s internal [graph](struct.Tournament.html#method.graph) is somehow malformed. This can be caused by manipulating the graph's structure after the tournament is instantiated.
	MalformedBracket,
	/// Returned when attempting to create a [`Tournament`](struct.Tournament.html) with zero entrants.
	NeedsAtLeastOneEntrant,
	/// Catchall other error.
	Other(&'static str),
	/// Returned by [`print_tournament`](fn.print_tournament.html) when some error prevents it from formatting the tree.
	PrintFailure,
}