bevy_flowfield_tiles_plugin/flowfields/fields/
mod.rs

1//! The kinds of fields used by the algorithm
2//!
3
4pub mod cost_field;
5pub mod flow_field;
6pub mod integration_field;
7
8use std::{collections::BTreeMap, time::Duration};
9
10use crate::prelude::*;
11use bevy::prelude::*;
12
13/// Defines required access to field arrays
14pub trait Field<T> {
15	/// Get a reference to the field array
16	fn get(&self) -> &[[T; FIELD_RESOLUTION]; FIELD_RESOLUTION];
17	/// Retrieve a field cell value
18	fn get_field_cell_value(&self, field_cell: FieldCell) -> T;
19	/// Set a field cell to a value
20	fn set_field_cell_value(&mut self, value: T, field_cell: FieldCell);
21}
22
23/// ID of a cell within a field
24#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
25#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash, Reflect)]
26pub struct FieldCell((usize, usize));
27
28impl std::fmt::Display for FieldCell {
29	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30		write!(f, "Column: {}, Row: {}", self.0 .0, self.0 .1)
31	}
32}
33
34impl FieldCell {
35	/// Create a new instance of [FieldCell]
36	pub fn new(column: usize, row: usize) -> Self {
37		FieldCell((column, row))
38	}
39	/// Get the sector `(column, row)` tuple
40	pub fn get_column_row(&self) -> (usize, usize) {
41		self.0
42	}
43	/// Get the sector column
44	pub fn get_column(&self) -> usize {
45		self.0 .0
46	}
47	/// Get the sector row
48	pub fn get_row(&self) -> usize {
49		self.0 .1
50	}
51	/// From the position of a `cell_id`, if it sits along a boundary, return the [Ordinal] of that boundary. Note that if the `cell_id` is in a field corner then it'll have two boundaries. Note that if the `cell_id` is not in fact along a boundary then this will panic
52	pub fn get_boundary_ordinal_from_field_cell(&self) -> Vec<Ordinal> {
53		let mut boundaries = Vec::new();
54		if self.get_row() == 0 {
55			boundaries.push(Ordinal::North);
56		}
57		if self.get_column() == FIELD_RESOLUTION - 1 {
58			boundaries.push(Ordinal::East);
59		}
60		if self.get_row() == FIELD_RESOLUTION - 1 {
61			boundaries.push(Ordinal::South);
62		}
63		if self.get_column() == 0 {
64			boundaries.push(Ordinal::West);
65		}
66		if !boundaries.is_empty() {
67			boundaries
68		} else {
69			panic!("{:?} does not sit along the boundary", self);
70		}
71	}
72	/// Using the Bresenham line algorithm get a list of [FieldCell] that lie along a line between two points. Note that the list will contain the source (`self`) and `target` [FieldCell]
73	pub fn get_cells_between_points(&self, target: &FieldCell) -> Vec<FieldCell> {
74		let source_col = self.get_column() as i32;
75		let source_row = self.get_row() as i32;
76		let target_col = target.get_column() as i32;
77		let target_row = target.get_row() as i32;
78
79		// optimise for orthognal line (horizontal or vertical)
80		if source_col == target_col {
81			let mut fields = Vec::new();
82			if source_row < target_row {
83				for row in source_row..=target_row {
84					fields.push(FieldCell::new(source_col as usize, row as usize));
85				}
86				fields
87			} else {
88				for row in target_row..=source_row {
89					fields.push(FieldCell::new(source_col as usize, row as usize));
90				}
91				fields.reverse(); //TODO would vecdeq be good for adding at index 0, no need to reverse
92				fields
93			}
94		} else if source_row == target_row {
95			let mut fields = Vec::new();
96			if source_col < target_col {
97				for col in source_col..=target_col {
98					fields.push(FieldCell::new(col as usize, source_row as usize));
99				}
100				fields
101			} else {
102				for col in target_col..=source_col {
103					fields.push(FieldCell::new(col as usize, source_row as usize));
104				}
105				fields.reverse();
106				fields
107			}
108		} else if (target_row - source_row).abs() < (target_col - source_col).abs() {
109			if source_col > target_col {
110				let mut fields =
111					walk_bresenham_shallow(target_col, target_row, source_col, source_row);
112				// ensure list points in the direction of source to target
113				fields.reverse();
114				fields
115			} else {
116				walk_bresenham_shallow(source_col, source_row, target_col, target_row)
117			}
118		} else if source_row > target_row {
119			let mut fields = walk_bresenham_steep(target_col, target_row, source_col, source_row);
120			fields.reverse();
121			fields
122		} else {
123			walk_bresenham_steep(source_col, source_row, target_col, target_row)
124		}
125	}
126}
127/// When finding a shallow raster representation of a line we step through the x-dimension and increment y based on an error bound which indicates which cells lie on the line
128fn walk_bresenham_shallow(col_0: i32, row_0: i32, col_1: i32, row_1: i32) -> Vec<FieldCell> {
129	let mut cells = Vec::new();
130
131	let delta_col = col_1 - col_0;
132	let mut delta_row = row_1 - row_0;
133
134	let mut row_increment = 1;
135	if delta_row < 0 {
136		row_increment = -1;
137		delta_row *= -1;
138	}
139	let mut difference = 2 * delta_row - delta_col;
140	let mut row = row_0;
141
142	for col in col_0..=col_1 {
143		cells.push(FieldCell::new(col as usize, row as usize));
144		if difference > 0 {
145			row += row_increment;
146			difference += 2 * (delta_row - delta_col);
147		} else {
148			difference += 2 * delta_row;
149		}
150	}
151	cells
152}
153/// When finding a steep raster representation of a line we step through the y-dimension and increment x based on an error bound which indicates which cells lie on the line
154fn walk_bresenham_steep(col_0: i32, row_0: i32, col_1: i32, row_1: i32) -> Vec<FieldCell> {
155	let mut cells = Vec::new();
156
157	let mut delta_col = col_1 - col_0;
158	let delta_row = row_1 - row_0;
159
160	let mut col_increment = 1;
161	if delta_col < 0 {
162		col_increment = -1;
163		delta_col *= -1;
164	}
165	let mut difference = 2 * delta_col - delta_row;
166	let mut col = col_0;
167
168	for row in row_0..=row_1 {
169		cells.push(FieldCell::new(col as usize, row as usize));
170		if difference > 0 {
171			col += col_increment;
172			difference += 2 * (delta_col - delta_row);
173		} else {
174			difference += 2 * delta_col;
175		}
176	}
177	cells
178}
179
180/// Describes the properties of a route
181#[derive(Clone, Copy, Debug, Reflect)]
182#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
183pub struct RouteMetadata {
184	/// Starting sector of the route
185	source_sector: SectorID,
186	/// Starting FieldCell of the route
187	source_field: FieldCell,
188	/// Sector to find a route to
189	target_sector: SectorID,
190	/// Field cell of the goal in the target sector
191	target_goal: FieldCell,
192	//? If a game is running for 136 years bad things will start happening here
193	/// Marks the route based on time elapsed since app start, used to enable automatic cleardown of long lived routes that are probably not needed anymore
194	time_generated: Duration,
195}
196// we don't want to compare `time_generated` so manually impl PartialEq
197impl PartialEq for RouteMetadata {
198	fn eq(&self, other: &Self) -> bool {
199		self.source_sector == other.source_sector
200			&& self.source_field == other.source_field
201			&& self.target_sector == other.target_sector
202			&& self.target_goal == other.target_goal
203	}
204}
205impl Eq for RouteMetadata {}
206
207impl Ord for RouteMetadata {
208	fn cmp(&self, other: &Self) -> std::cmp::Ordering {
209		(
210			self.source_sector,
211			self.source_field,
212			self.target_sector,
213			self.target_goal,
214		)
215			.cmp(&(
216				other.source_sector,
217				other.source_field,
218				other.target_sector,
219				other.target_goal,
220			))
221	}
222}
223
224impl PartialOrd for RouteMetadata {
225	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
226		Some(self.cmp(other))
227	}
228}
229
230impl RouteMetadata {
231	/// Create a new [RouteMetadata]
232	pub fn new(
233		source_sector: SectorID,
234		source_field: FieldCell,
235		target_sector: SectorID,
236		target_goal: FieldCell,
237		time_generated: Duration,
238	) -> Self {
239		RouteMetadata {
240			source_sector,
241			source_field,
242			target_sector,
243			target_goal,
244			time_generated,
245		}
246	}
247	/// Get the source sector
248	pub fn get_source_sector(&self) -> SectorID {
249		self.source_sector
250	}
251	/// Get the source FieldCell
252	pub fn get_source_field_cell(&self) -> FieldCell {
253		self.source_field
254	}
255	/// Get the target sector
256	pub fn get_target_sector(&self) -> SectorID {
257		self.target_sector
258	}
259	/// Get the goal
260	pub fn get_target_goal(&self) -> FieldCell {
261		self.target_goal
262	}
263	/// Get when the route was generated
264	pub fn get_time_generated(&self) -> Duration {
265		self.time_generated
266	}
267}
268
269/// List of sector-portal (or just the end goal) route describing the sector path an actor should take to move to a destination sector
270#[derive(Default, Clone, Debug, Reflect)]
271#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
272pub struct Route(Vec<(SectorID, FieldCell)>);
273
274impl Route {
275	/// Get the sector to sector path including portals/goals
276	pub fn get(&self) -> &Vec<(SectorID, FieldCell)> {
277		&self.0
278	}
279	/// Get a mutable reference to the sector to sector path including portals/goals
280	pub fn get_mut(&mut self) -> &mut Vec<(SectorID, FieldCell)> {
281		&mut self.0
282	}
283	/// Create a new instance of [Route] with the given `path`
284	pub fn new(path: Vec<(SectorID, FieldCell)>) -> Self {
285		Route(path)
286	}
287}
288
289/// Each key makes use of custom Ord and Eq implementations based on comparing `(source_id, target_id, goal_id)` so that RouteMetaData can be used to refer to the high-level route an actor has asked for. The value is a sector-portal (or just the end goal) route. An actor can use this as a fallback if the `field_cache` doesn't yet contain the granular [FlowField] routes or for when [CostField]s have been changed and so [FlowField]s in the cache need to be regenerated
290#[derive(Component, Default, Clone, Reflect)]
291#[reflect(Component)]
292#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
293pub struct RouteCache {
294	/// A queue of high-level routes which get processed into the `routes` field
295	route_queue: BTreeMap<RouteMetadata, Route>,
296	/// High-level routes describing the path from an actor to an end goal
297	routes: BTreeMap<RouteMetadata, Route>,
298}
299
300impl RouteCache {
301	/// Get a refernce to the map of queued routes
302	pub fn get_queue(&self) -> &BTreeMap<RouteMetadata, Route> {
303		&self.route_queue
304	}
305	/// Get a mutable reference to the map of queued routes
306	pub fn get_queue_mut(&mut self) -> &mut BTreeMap<RouteMetadata, Route> {
307		&mut self.route_queue
308	}
309	/// Get the map of routes
310	pub fn get_routes(&self) -> &BTreeMap<RouteMetadata, Route> {
311		&self.routes
312	}
313	/// Get a mutable reference to the map of routes
314	pub fn get_mut(&mut self) -> &mut BTreeMap<RouteMetadata, Route> {
315		&mut self.routes
316	}
317	/// Get a high-level sector to sector route. Returns [None] if it doesn't exist
318	pub fn get_route(
319		&self,
320		source_sector: SectorID,
321		source_field: FieldCell,
322		target_sector: SectorID,
323		goal_id: FieldCell,
324	) -> Option<&Route> {
325		let route_data = RouteMetadata {
326			source_sector,
327			source_field,
328			target_sector,
329			target_goal: goal_id,
330			time_generated: Duration::default(),
331		};
332		let route = self.routes.get(&route_data);
333		route
334	}
335	/// Get a high-level sector to sector route. Returns [None] if it doesn't exist
336	pub fn get_route_with_metadata(
337		&self,
338		source_sector: SectorID,
339		source_field: FieldCell,
340		target_sector: SectorID,
341		goal_id: FieldCell,
342	) -> Option<(&RouteMetadata, &Route)> {
343		let route_data = RouteMetadata {
344			source_sector,
345			source_field,
346			target_sector,
347			target_goal: goal_id,
348			time_generated: Duration::default(),
349		};
350		let route = self.routes.get_key_value(&route_data);
351		route
352	}
353	/// Insert a high-level route of sector-portal paths (or just the end goal if local sector pathing) into the `route_cache`
354	pub fn add_to_queue(&mut self, route_data: RouteMetadata, route: Route) {
355		self.route_queue.insert(route_data, route);
356	}
357	/// Insert a high-level route of sector-portal paths (or just the end goal if local sector pathing) into the `route_cache`
358	pub fn insert_route(
359		&mut self,
360		source_sector: SectorID,
361		source_field: FieldCell,
362		target_sector: SectorID,
363		goal_id: FieldCell,
364		elapsed_duration: Duration,
365		route: Route,
366	) {
367		let route_data = RouteMetadata {
368			source_sector,
369			source_field,
370			target_sector,
371			target_goal: goal_id,
372			time_generated: elapsed_duration,
373		};
374		self.routes.insert(route_data, route);
375	}
376	/// Insert a high-level route of sector-portal paths (or just the end goal if local sector pathing) into the `route_cache` with an already created [RouteMetadata] structure
377	pub fn insert_route_with_metadata(&mut self, route_metadata: RouteMetadata, route: Route) {
378		self.routes.insert(route_metadata, route);
379	}
380	/// Remove a high-level  route of sector-portal paths (or just the end goal if local sector pathing) from the `route_cache`
381	pub fn remove_route(&mut self, route_metadata: RouteMetadata) {
382		self.routes.remove(&route_metadata);
383	}
384	/// Remove a high-level route that has been queued (or just the end goal if
385	/// local sector pathing)
386	pub fn remove_queued_route(&mut self, route_metadata: RouteMetadata) {
387		self.route_queue.remove(&route_metadata);
388	}
389}
390/// Describes the properties of a [FlowField]
391#[derive(Clone, Copy, Reflect)]
392#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
393pub struct FlowFieldMetadata {
394	/// The sector of the corresponding [FlowField]
395	sector_id: SectorID,
396	/// Goal ID if this is the field of the terminus sector
397	goal_id: Option<FieldCell>,
398	/// Portal ID if this field is used in trnasit to another sector
399	portal_id: Option<FieldCell>,
400	//? If a game is running for 136 years bad things will start happening here
401	/// Marks the field based on time elapsed since app start, used to enable automatic cleardown of long lived fields that are probably not needed anymore
402	time_generated: Duration,
403}
404// we don't want to compare `time_generated` so manually impl PartialEq
405impl PartialEq for FlowFieldMetadata {
406	fn eq(&self, other: &Self) -> bool {
407		self.sector_id == other.sector_id
408			&& self.goal_id == other.goal_id
409			&& self.portal_id == other.portal_id
410	}
411}
412impl Eq for FlowFieldMetadata {}
413impl Ord for FlowFieldMetadata {
414	fn cmp(&self, other: &Self) -> std::cmp::Ordering {
415		(self.sector_id, self.goal_id, self.portal_id).cmp(&(
416			other.sector_id,
417			other.goal_id,
418			other.portal_id,
419		))
420	}
421}
422impl PartialOrd for FlowFieldMetadata {
423	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
424		Some(self.cmp(other))
425	}
426}
427
428impl FlowFieldMetadata {
429	/// Get the sector
430	pub fn get_sector_id(&self) -> SectorID {
431		self.sector_id
432	}
433	/// Get the goal
434	pub fn get_goal_id(&self) -> Option<FieldCell> {
435		self.goal_id
436	}
437	/// Get the portal
438	pub fn get_portal_id(&self) -> Option<FieldCell> {
439		self.portal_id
440	}
441	/// Get when the field was generated
442	pub fn get_time_generated(&self) -> Duration {
443		self.time_generated
444	}
445}
446
447/// Each generated [FlowField] is placed into this cache so that multiple actors can read from the same dataset.
448///
449/// Each entry is given an ID of `(sector_id, goal_id)` and actors can poll the
450/// cache to retrieve the field once it's built and inserted. Note that
451/// `goal_id` can refer to the true end-goal or it can refer to a portal
452/// position when a path spans multiple sectors
453#[derive(Component, Default, Reflect)]
454#[reflect(Component)]
455#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
456pub struct FlowFieldCache {
457	/// Routes describing the sector path and [IntegrationField]s where the
458	/// integration and flow fields can be incrementally built
459	queue: BTreeMap<RouteMetadata, IntegrationBuilder>,
460	/// Created FlowFields that actors can use to pathfind
461	flows: BTreeMap<FlowFieldMetadata, FlowField>,
462}
463
464impl FlowFieldCache {
465	/// Get the map of [FlowField]s
466	pub fn get(&self) -> &BTreeMap<FlowFieldMetadata, FlowField> {
467		&self.flows
468	}
469	/// Get a mutable reference to the map of [FlowField]s
470	pub fn get_mut(&mut self) -> &mut BTreeMap<FlowFieldMetadata, FlowField> {
471		&mut self.flows
472	}
473	/// Get a mutable reference to the queue map
474	pub fn get_queue_mut(&mut self) -> &mut BTreeMap<RouteMetadata, IntegrationBuilder> {
475		&mut self.queue
476	}
477	/// Insert a route into the queue to be built
478	pub fn add_to_queue(
479		&mut self,
480		metadata: RouteMetadata,
481		path: Route,
482		cost_fields: &SectorCostFields,
483	) {
484		let int_builder = IntegrationBuilder::new(path, cost_fields);
485		self.queue.insert(metadata, int_builder);
486	}
487	/// Get a [FlowField] based on the `sector_id` and `goal_id`. Returns
488	/// [None] if the cache doesn't contain a record
489	pub fn get_field(
490		&self,
491		current_sector_id: SectorID,
492		goal_sector_id: SectorID,
493		goal_id: FieldCell,
494	) -> Option<&FlowField> {
495		if current_sector_id == goal_sector_id {
496			let flow_meta = FlowFieldMetadata {
497				sector_id: current_sector_id,
498				goal_id: Some(goal_id),
499				portal_id: None,
500				time_generated: Duration::default(),
501			};
502			self.flows.get(&flow_meta)
503		} else {
504			let flow_meta = FlowFieldMetadata {
505				sector_id: current_sector_id,
506				goal_id: None,
507				portal_id: Some(goal_id),
508				time_generated: Duration::default(),
509			};
510			self.flows.get(&flow_meta)
511		}
512	}
513	/// Insert a [FlowField] into the cache with a sector-goal ID
514	pub fn insert_field(
515		&mut self,
516		sector_id: SectorID,
517		goal_id: Option<FieldCell>,
518		portal_id: Option<FieldCell>,
519		elapsed_duration: Duration,
520		field: FlowField,
521	) {
522		let flow_meta = FlowFieldMetadata {
523			sector_id,
524			goal_id,
525			portal_id,
526			time_generated: elapsed_duration,
527		};
528		self.flows.insert(flow_meta, field);
529	}
530	/// Remove a [FlowField] from the cache (when it needs regenerating from a
531	/// [CostField] update)
532	pub fn remove_field(&mut self, flow_meta: FlowFieldMetadata) {
533		self.flows.remove(&flow_meta);
534	}
535	/// Remove a [RouteMetadata] from the cache integration queue (when it
536	/// needs regenerating from a [CostField] update)
537	pub fn remove_queue_item(&mut self, route_meta: RouteMetadata) {
538		self.queue.remove(&route_meta);
539	}
540}
541
542#[cfg(test)]
543mod tests {
544	use super::*;
545	#[test]
546	fn field_cell_line_horizontal() {
547		let source = FieldCell::new(3, 4);
548		let target = FieldCell::new(7, 4);
549		let result = source.get_cells_between_points(&target);
550		let actual: Vec<FieldCell> = vec![
551			FieldCell::new(3, 4),
552			FieldCell::new(4, 4),
553			FieldCell::new(5, 4),
554			FieldCell::new(6, 4),
555			FieldCell::new(7, 4),
556		];
557		assert_eq!(actual, result);
558	}
559	#[test]
560	fn field_cell_line_horizontal_reverse() {
561		let source = FieldCell::new(7, 4);
562		let target = FieldCell::new(3, 4);
563		let result = source.get_cells_between_points(&target);
564		let actual: Vec<FieldCell> = vec![
565			FieldCell::new(7, 4),
566			FieldCell::new(6, 4),
567			FieldCell::new(5, 4),
568			FieldCell::new(4, 4),
569			FieldCell::new(3, 4),
570		];
571		assert_eq!(actual, result);
572	}
573	#[test]
574	fn field_cell_line_vertical() {
575		let source = FieldCell::new(3, 4);
576		let target = FieldCell::new(3, 7);
577		let result = source.get_cells_between_points(&target);
578		let actual: Vec<FieldCell> = vec![
579			FieldCell::new(3, 4),
580			FieldCell::new(3, 5),
581			FieldCell::new(3, 6),
582			FieldCell::new(3, 7),
583		];
584		assert_eq!(actual, result);
585	}
586	#[test]
587	fn field_cell_line_vertical_reverse() {
588		let source = FieldCell::new(3, 7);
589		let target = FieldCell::new(3, 4);
590		let result = source.get_cells_between_points(&target);
591		let actual: Vec<FieldCell> = vec![
592			FieldCell::new(3, 7),
593			FieldCell::new(3, 6),
594			FieldCell::new(3, 5),
595			FieldCell::new(3, 4),
596		];
597		assert_eq!(actual, result);
598	}
599	#[test]
600	fn field_cell_line_vertical_steep() {
601		let source = FieldCell::new(3, 0);
602		let target = FieldCell::new(4, 9);
603		let result = source.get_cells_between_points(&target);
604		let actual: Vec<FieldCell> = vec![
605			FieldCell::new(3, 0),
606			FieldCell::new(3, 1),
607			FieldCell::new(3, 2),
608			FieldCell::new(3, 3),
609			FieldCell::new(3, 4),
610			FieldCell::new(4, 5),
611			FieldCell::new(4, 6),
612			FieldCell::new(4, 7),
613			FieldCell::new(4, 8),
614			FieldCell::new(4, 9),
615		];
616		assert_eq!(actual, result);
617	}
618	#[test]
619	fn field_cell_line_pos_gradient() {
620		let source = FieldCell::new(3, 4);
621		let target = FieldCell::new(7, 6);
622		let result = source.get_cells_between_points(&target);
623		let actual: Vec<FieldCell> = vec![
624			FieldCell::new(3, 4),
625			FieldCell::new(4, 4),
626			FieldCell::new(5, 5),
627			FieldCell::new(6, 5),
628			FieldCell::new(7, 6),
629		];
630		assert_eq!(actual, result);
631	}
632	#[test]
633	fn field_cell_line_pos_gradient_reverse() {
634		let source = FieldCell::new(7, 6);
635		let target = FieldCell::new(3, 4);
636		let result = source.get_cells_between_points(&target);
637		let actual: Vec<FieldCell> = vec![
638			FieldCell::new(7, 6),
639			FieldCell::new(6, 5),
640			FieldCell::new(5, 5),
641			FieldCell::new(4, 4),
642			FieldCell::new(3, 4),
643		];
644		assert_eq!(actual, result);
645	}
646	#[test]
647	fn field_cell_line_neg_gradient() {
648		let source = FieldCell::new(3, 4);
649		let target = FieldCell::new(7, 2);
650		let result = source.get_cells_between_points(&target);
651		let actual: Vec<FieldCell> = vec![
652			FieldCell::new(3, 4),
653			FieldCell::new(4, 4),
654			FieldCell::new(5, 3),
655			FieldCell::new(6, 3),
656			FieldCell::new(7, 2),
657		];
658		assert_eq!(actual, result);
659	}
660	#[test]
661	fn field_cell_line_neg_gradient_reverse() {
662		let source = FieldCell::new(7, 2);
663		let target = FieldCell::new(3, 4);
664		let result = source.get_cells_between_points(&target);
665		let actual: Vec<FieldCell> = vec![
666			FieldCell::new(7, 2),
667			FieldCell::new(6, 3),
668			FieldCell::new(5, 3),
669			FieldCell::new(4, 4),
670			FieldCell::new(3, 4),
671		];
672		assert_eq!(actual, result);
673	}
674	#[test]
675	fn field_cell_line_zero() {
676		let source = FieldCell::new(3, 4);
677		let target = FieldCell::new(3, 4);
678		let result = source.get_cells_between_points(&target);
679		let actual: Vec<FieldCell> = vec![FieldCell::new(3, 4)];
680		assert_eq!(actual, result);
681	}
682}