Skip to content

Commit 29a0d47

Browse files
Add get_field_info to table
1 parent 2cdfdb9 commit 29a0d47

File tree

1 file changed

+157
-39
lines changed
  • minesweeper/src/minesweeper_logic

1 file changed

+157
-39
lines changed

minesweeper/src/minesweeper_logic/table.rs

Lines changed: 157 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ static OPENED_FIELD_CAN_NOT_BE_UPDATED_ERROR: &'static str = "An opened field ca
1818
trait Field {
1919
fn get_field_state(&self) -> FieldState;
2020
fn get_field_type(&self) -> FieldType;
21+
fn get_public_field_info(&self) -> FieldInfo;
2122
}
2223

2324
#[automock]
2425
pub trait Table {
2526
fn width(&self) -> SizeType;
2627
fn height(&self) -> SizeType;
28+
fn get_field_info(&self, row: SizeType, col: SizeType) -> Result<FieldInfo, &'static str>;
2729
fn open_field(&mut self, row: SizeType, col: SizeType) -> Result<OpenInfo, &'static str>;
2830
fn open_neighbors(&mut self, row: SizeType, col: SizeType) -> Result<OpenInfo, &'static str>;
2931
fn toggle_flag(&mut self, row: SizeType, col: SizeType) -> Result<FlagResult, &'static str>;
@@ -144,6 +146,16 @@ impl Field for FieldInner {
144146
fn get_field_type(&self) -> FieldType {
145147
self.field_info.field_type.clone()
146148
}
149+
150+
fn get_public_field_info(&self) -> FieldInfo {
151+
match self.field_info.state {
152+
FieldState::Opened => self.field_info.clone(),
153+
state => FieldInfo {
154+
state,
155+
field_type: FieldType::Empty,
156+
},
157+
}
158+
}
147159
}
148160

149161
struct FieldVisiter {
@@ -379,28 +391,34 @@ impl BasicTable {
379391
== (self.mine_locations.len() as SizeType).checked_add(self.number_of_opened_fields)
380392
}
381393

394+
fn get_field_unchecked(&self, row: SizeType, col: SizeType) -> &FieldInner {
395+
&self.fields[row as usize][col as usize]
396+
}
397+
398+
fn get_field_unchecked_mut(&mut self, row: SizeType, col: SizeType) -> &mut FieldInner {
399+
&mut self.fields[row as usize][col as usize]
400+
}
401+
382402
fn move_mine(&mut self, row: SizeType, col: SizeType) -> Result<(), &'static str> {
383-
if self.fields[row as usize][col as usize]
403+
if self
404+
.get_field_unchecked(row, col)
384405
.get_field_type()
385406
.is_mine()
386407
{
387408
let mut new_place = (0, 0);
388409
let mut visiter = FieldVisiter::new(self.height, self.width, row, col)?;
389410
while let Some((r, c)) = visiter.next() {
390-
if !self.fields[r as usize][c as usize]
391-
.get_field_type()
392-
.is_mine()
393-
{
411+
if !self.get_field_unchecked(r, c).get_field_type().is_mine() {
394412
new_place = (r, c);
395413
break;
396414
}
397415
visiter.extend_with_unvisited_neighbors(r, c);
398416
}
399417

400-
self.fields[new_place.0 as usize][new_place.1 as usize]
418+
self.get_field_unchecked_mut(new_place.0, new_place.1)
401419
.update_type_to_mine()
402420
.unwrap();
403-
self.fields[row as usize][col as usize]
421+
self.get_field_unchecked_mut(row, col)
404422
.update_type_to_empty()
405423
.unwrap();
406424
self.mine_locations.remove(&(row, col));
@@ -413,16 +431,15 @@ impl BasicTable {
413431
);
414432
fields_to_recalculate.insert((row, col));
415433
for (r, c) in fields_to_recalculate {
416-
if !self.fields[r as usize][c as usize]
417-
.get_field_type()
418-
.is_mine()
419-
{
434+
if !self.get_field_unchecked(r, c).get_field_type().is_mine() {
420435
let field_value = self.get_field_value(r, c).unwrap();
421436
match field_value {
422-
0 => self.fields[r as usize][c as usize]
437+
0 => self
438+
.get_field_unchecked_mut(r, c)
423439
.update_type_to_empty()
424440
.unwrap(),
425-
_ => self.fields[r as usize][c as usize]
441+
_ => self
442+
.get_field_unchecked_mut(r, c)
426443
.update_type_with_value(field_value)
427444
.unwrap(),
428445
};
@@ -442,7 +459,7 @@ impl BasicTable {
442459
for column in 0..self.width {
443460
boom_result.newly_opened_fields.insert(
444461
(row, column),
445-
self.fields[row as usize][column as usize].get_field_type(),
462+
self.get_field_unchecked(row, column).get_field_type(),
446463
);
447464
}
448465
}
@@ -454,7 +471,7 @@ impl BasicTable {
454471
let mut has_boomed = false;
455472

456473
while let Some((r, c)) = visiter.next() {
457-
match self.fields[r as usize][c as usize].open() {
474+
match self.get_field_unchecked_mut(r, c).open() {
458475
FieldOpenResult::MultiOpen => {
459476
self.number_of_opened_fields = self.number_of_opened_fields + 1;
460477
visiter.extend_with_unvisited_neighbors(r, c);
@@ -466,8 +483,7 @@ impl BasicTable {
466483
_ => continue,
467484
};
468485

469-
newly_opened_fields
470-
.insert((r, c), self.fields[r as usize][c as usize].get_field_type());
486+
newly_opened_fields.insert((r, c), self.get_field_unchecked(r, c).get_field_type());
471487
}
472488

473489
if has_boomed {
@@ -476,10 +492,7 @@ impl BasicTable {
476492

477493
if self.all_fields_are_open() {
478494
for mine_coords in &self.mine_locations {
479-
newly_opened_fields.insert(
480-
(mine_coords.0, mine_coords.1),
481-
self.fields[mine_coords.0 as usize][mine_coords.1 as usize].get_field_type(),
482-
);
495+
newly_opened_fields.insert((mine_coords.0, mine_coords.1), FieldType::Mine);
483496
}
484497
Ok(OpenInfo {
485498
result: OpenResult::WINNER,
@@ -503,7 +516,7 @@ impl BasicTable {
503516
if row == r && col == c {
504517
continue;
505518
}
506-
if self.fields[r as usize][c as usize].get_field_state() == FieldState::Flagged {
519+
if self.get_field_unchecked(r, c).get_field_state() == FieldState::Flagged {
507520
number_of_flagged_neighbors += 1;
508521
}
509522
}
@@ -528,10 +541,16 @@ impl Table for BasicTable {
528541
self.height
529542
}
530543

544+
fn get_field_info(&self, row: SizeType, col: SizeType) -> Result<FieldInfo, &'static str> {
545+
self.validate_indices(row, col)?;
546+
Ok(self.get_field_unchecked(row, col).get_public_field_info())
547+
}
548+
531549
fn open_field(&mut self, row: SizeType, col: SizeType) -> Result<OpenInfo, &'static str> {
532550
self.validate_indices(row, col)?;
533551

534-
if self.fields[row as usize][col as usize]
552+
if self
553+
.get_field_unchecked(row, col)
535554
.get_field_state()
536555
.is_flagged()
537556
{
@@ -542,7 +561,8 @@ impl Table for BasicTable {
542561
}
543562

544563
if self.number_of_opened_fields == 0
545-
&& self.fields[row as usize][col as usize]
564+
&& self
565+
.get_field_unchecked(row, col)
546566
.get_field_type()
547567
.is_mine()
548568
{
@@ -561,14 +581,15 @@ impl Table for BasicTable {
561581
newly_opened_fields: HashMap::new(),
562582
};
563583

564-
if !self.fields[row as usize][col as usize]
584+
if !self
585+
.get_field_unchecked(row, col)
565586
.get_field_state()
566587
.is_opened()
567588
{
568589
return Ok(empty_open_info);
569590
}
570591

571-
match self.fields[row as usize][col as usize].get_field_type() {
592+
match self.get_field_unchecked(row, col).get_field_type() {
572593
FieldType::Numbered(x) if x == self.count_flagged_neighbors(row, col)? => {
573594
let mut visiter = FieldVisiter::new(self.height, self.width, row, col)?;
574595
visiter.extend_with_unvisited_neighbors(row, col);
@@ -581,7 +602,7 @@ impl Table for BasicTable {
581602
fn toggle_flag(&mut self, row: SizeType, col: SizeType) -> Result<FlagResult, &'static str> {
582603
self.validate_indices(row, col)?;
583604

584-
Ok(self.fields[row as usize][col as usize].toggle_flag())
605+
Ok(self.get_field_unchecked_mut(row, col).toggle_flag())
585606
}
586607
}
587608

@@ -610,12 +631,13 @@ mod test {
610631
mine_locations: HashSet<(SizeType, SizeType)>,
611632
fields: Vec<Vec<FieldType>>,
612633
}
613-
614-
// O O 1 M 1 0
615-
// O 1 2 2 1 0
616-
// 1 2 M 2 1 0
617-
// M 3 3 M 1 0
618-
// 2 M 2 1 1 0
634+
// 0 1 2 3 4 5
635+
// - - - - - -
636+
// 0 | O O 1 M 1 0
637+
// 1 | O 1 2 2 1 0
638+
// 2 | 1 2 M 2 1 0
639+
// 3 | M 3 3 M 1 0
640+
// 4 | 2 M 2 1 1 0
619641
fn create_test_info_5x6() -> TestInfo {
620642
let fields = vec![
621643
vec![
@@ -791,12 +813,14 @@ mod test {
791813
}
792814

793815
#[test]
794-
// 1 2 3 2 3 M
795-
// 2 M M M 5 M
796-
// 3 M 8 M 6 M
797-
// 2 M M M 5 M
798-
// 1 2 3 2 4 M
799-
// 0 0 0 0 2 M
816+
// 0 1 2 3 4 5
817+
// - - - - - -
818+
// 0 | 1 2 3 2 3 M
819+
// 1 | 2 M M M 5 M
820+
// 2 | 3 M 8 M 6 M
821+
// 3 | 2 M M M 5 M
822+
// 4 | 1 2 3 2 4 M
823+
// 5 | 0 0 0 0 2 M
800824
fn get_value_general_test() {
801825
let width = 6;
802826
let height = 6;
@@ -1355,5 +1379,99 @@ mod test {
13551379
check_indices(-1, -1, "Negative both");
13561380
}
13571381

1382+
#[test]
1383+
fn get_field_info_closed() {
1384+
let test_info = create_test_info_5x6();
1385+
let table = test_info.table.borrow_mut();
1386+
let expected_field_info = FieldInfo {
1387+
state: FieldState::Closed,
1388+
field_type: FieldType::Empty,
1389+
};
1390+
for row in 0..test_info.height {
1391+
for col in 0..test_info.width {
1392+
let field_info = table.get_field_info(row, col).unwrap();
1393+
assert_eq!(expected_field_info, field_info);
1394+
}
1395+
}
1396+
}
1397+
1398+
#[test]
1399+
fn get_field_info_flagged() {
1400+
let test_info = create_test_info_5x6();
1401+
let mut table = test_info.table.borrow_mut();
1402+
let expected_field_info = FieldInfo {
1403+
state: FieldState::Flagged,
1404+
field_type: FieldType::Empty,
1405+
};
1406+
for row in 0..test_info.height {
1407+
for col in 0..test_info.width {
1408+
table.toggle_flag(row, col).unwrap();
1409+
let field_info = table.get_field_info(row, col).unwrap();
1410+
assert_eq!(expected_field_info, field_info);
1411+
}
1412+
}
1413+
}
1414+
1415+
#[test]
1416+
fn get_field_info_opened() {
1417+
let test_info = create_test_info_5x6();
1418+
let mut table = test_info.table.borrow_mut();
1419+
for row in 0..test_info.height {
1420+
for col in 0..test_info.width {
1421+
table.open_field(row, col).unwrap();
1422+
let field_info = table.get_field_info(row, col).unwrap();
1423+
let expected_field_info = FieldInfo {
1424+
state: FieldState::Opened,
1425+
field_type: test_info.fields[row as usize][col as usize].clone(),
1426+
};
1427+
assert_eq!(expected_field_info, field_info);
1428+
}
1429+
}
1430+
}
1431+
1432+
#[test]
1433+
fn get_field_info_mixed() {
1434+
let test_info = create_test_info_5x6();
1435+
let mut table = test_info.table.borrow_mut();
1436+
table.open_field(0, 0).unwrap();
1437+
let closed_field_info = FieldInfo {
1438+
state: FieldState::Closed,
1439+
field_type: FieldType::Empty,
1440+
};
1441+
1442+
for row in 0..test_info.height {
1443+
for col in 0..test_info.width {
1444+
let field_info = table.get_field_info(row, col).unwrap();
1445+
if row + col <= 3 && row < 3 && col < 3 {
1446+
let expected_field_info = FieldInfo {
1447+
state: FieldState::Opened,
1448+
field_type: test_info.fields[row as usize][col as usize].clone(),
1449+
};
1450+
assert_eq!(expected_field_info, field_info);
1451+
} else {
1452+
assert_eq!(closed_field_info, field_info);
1453+
}
1454+
}
1455+
}
1456+
1457+
table.open_field(3, 0).unwrap();
1458+
table.open_field(0, 3).unwrap();
1459+
1460+
for row in 0..test_info.height {
1461+
for col in 0..test_info.width {
1462+
let field_info = table.get_field_info(row, col).unwrap();
1463+
if row + col <= 3 {
1464+
let expected_field_info = FieldInfo {
1465+
state: FieldState::Opened,
1466+
field_type: test_info.fields[row as usize][col as usize].clone(),
1467+
};
1468+
assert_eq!(expected_field_info, field_info);
1469+
} else {
1470+
assert_eq!(closed_field_info, field_info);
1471+
}
1472+
}
1473+
}
1474+
}
1475+
13581476
// TODO Write test to full game
13591477
}

0 commit comments

Comments
 (0)