@@ -18,12 +18,14 @@ static OPENED_FIELD_CAN_NOT_BE_UPDATED_ERROR: &'static str = "An opened field ca
18
18
trait Field {
19
19
fn get_field_state ( & self ) -> FieldState ;
20
20
fn get_field_type ( & self ) -> FieldType ;
21
+ fn get_public_field_info ( & self ) -> FieldInfo ;
21
22
}
22
23
23
24
#[ automock]
24
25
pub trait Table {
25
26
fn width ( & self ) -> SizeType ;
26
27
fn height ( & self ) -> SizeType ;
28
+ fn get_field_info ( & self , row : SizeType , col : SizeType ) -> Result < FieldInfo , & ' static str > ;
27
29
fn open_field ( & mut self , row : SizeType , col : SizeType ) -> Result < OpenInfo , & ' static str > ;
28
30
fn open_neighbors ( & mut self , row : SizeType , col : SizeType ) -> Result < OpenInfo , & ' static str > ;
29
31
fn toggle_flag ( & mut self , row : SizeType , col : SizeType ) -> Result < FlagResult , & ' static str > ;
@@ -144,6 +146,16 @@ impl Field for FieldInner {
144
146
fn get_field_type ( & self ) -> FieldType {
145
147
self . field_info . field_type . clone ( )
146
148
}
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
+ }
147
159
}
148
160
149
161
struct FieldVisiter {
@@ -379,28 +391,34 @@ impl BasicTable {
379
391
== ( self . mine_locations . len ( ) as SizeType ) . checked_add ( self . number_of_opened_fields )
380
392
}
381
393
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
+
382
402
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)
384
405
. get_field_type ( )
385
406
. is_mine ( )
386
407
{
387
408
let mut new_place = ( 0 , 0 ) ;
388
409
let mut visiter = FieldVisiter :: new ( self . height , self . width , row, col) ?;
389
410
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 ( ) {
394
412
new_place = ( r, c) ;
395
413
break ;
396
414
}
397
415
visiter. extend_with_unvisited_neighbors ( r, c) ;
398
416
}
399
417
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 )
401
419
. update_type_to_mine ( )
402
420
. unwrap ( ) ;
403
- self . fields [ row as usize ] [ col as usize ]
421
+ self . get_field_unchecked_mut ( row, col)
404
422
. update_type_to_empty ( )
405
423
. unwrap ( ) ;
406
424
self . mine_locations . remove ( & ( row, col) ) ;
@@ -413,16 +431,15 @@ impl BasicTable {
413
431
) ;
414
432
fields_to_recalculate. insert ( ( row, col) ) ;
415
433
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 ( ) {
420
435
let field_value = self . get_field_value ( r, c) . unwrap ( ) ;
421
436
match field_value {
422
- 0 => self . fields [ r as usize ] [ c as usize ]
437
+ 0 => self
438
+ . get_field_unchecked_mut ( r, c)
423
439
. update_type_to_empty ( )
424
440
. unwrap ( ) ,
425
- _ => self . fields [ r as usize ] [ c as usize ]
441
+ _ => self
442
+ . get_field_unchecked_mut ( r, c)
426
443
. update_type_with_value ( field_value)
427
444
. unwrap ( ) ,
428
445
} ;
@@ -442,7 +459,7 @@ impl BasicTable {
442
459
for column in 0 ..self . width {
443
460
boom_result. newly_opened_fields . insert (
444
461
( row, column) ,
445
- self . fields [ row as usize ] [ column as usize ] . get_field_type ( ) ,
462
+ self . get_field_unchecked ( row, column) . get_field_type ( ) ,
446
463
) ;
447
464
}
448
465
}
@@ -454,7 +471,7 @@ impl BasicTable {
454
471
let mut has_boomed = false ;
455
472
456
473
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 ( ) {
458
475
FieldOpenResult :: MultiOpen => {
459
476
self . number_of_opened_fields = self . number_of_opened_fields + 1 ;
460
477
visiter. extend_with_unvisited_neighbors ( r, c) ;
@@ -466,8 +483,7 @@ impl BasicTable {
466
483
_ => continue ,
467
484
} ;
468
485
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 ( ) ) ;
471
487
}
472
488
473
489
if has_boomed {
@@ -476,10 +492,7 @@ impl BasicTable {
476
492
477
493
if self . all_fields_are_open ( ) {
478
494
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 ) ;
483
496
}
484
497
Ok ( OpenInfo {
485
498
result : OpenResult :: WINNER ,
@@ -503,7 +516,7 @@ impl BasicTable {
503
516
if row == r && col == c {
504
517
continue ;
505
518
}
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 {
507
520
number_of_flagged_neighbors += 1 ;
508
521
}
509
522
}
@@ -528,10 +541,16 @@ impl Table for BasicTable {
528
541
self . height
529
542
}
530
543
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
+
531
549
fn open_field ( & mut self , row : SizeType , col : SizeType ) -> Result < OpenInfo , & ' static str > {
532
550
self . validate_indices ( row, col) ?;
533
551
534
- if self . fields [ row as usize ] [ col as usize ]
552
+ if self
553
+ . get_field_unchecked ( row, col)
535
554
. get_field_state ( )
536
555
. is_flagged ( )
537
556
{
@@ -542,7 +561,8 @@ impl Table for BasicTable {
542
561
}
543
562
544
563
if self . number_of_opened_fields == 0
545
- && self . fields [ row as usize ] [ col as usize ]
564
+ && self
565
+ . get_field_unchecked ( row, col)
546
566
. get_field_type ( )
547
567
. is_mine ( )
548
568
{
@@ -561,14 +581,15 @@ impl Table for BasicTable {
561
581
newly_opened_fields : HashMap :: new ( ) ,
562
582
} ;
563
583
564
- if !self . fields [ row as usize ] [ col as usize ]
584
+ if !self
585
+ . get_field_unchecked ( row, col)
565
586
. get_field_state ( )
566
587
. is_opened ( )
567
588
{
568
589
return Ok ( empty_open_info) ;
569
590
}
570
591
571
- match self . fields [ row as usize ] [ col as usize ] . get_field_type ( ) {
592
+ match self . get_field_unchecked ( row, col) . get_field_type ( ) {
572
593
FieldType :: Numbered ( x) if x == self . count_flagged_neighbors ( row, col) ? => {
573
594
let mut visiter = FieldVisiter :: new ( self . height , self . width , row, col) ?;
574
595
visiter. extend_with_unvisited_neighbors ( row, col) ;
@@ -581,7 +602,7 @@ impl Table for BasicTable {
581
602
fn toggle_flag ( & mut self , row : SizeType , col : SizeType ) -> Result < FlagResult , & ' static str > {
582
603
self . validate_indices ( row, col) ?;
583
604
584
- Ok ( self . fields [ row as usize ] [ col as usize ] . toggle_flag ( ) )
605
+ Ok ( self . get_field_unchecked_mut ( row, col) . toggle_flag ( ) )
585
606
}
586
607
}
587
608
@@ -610,12 +631,13 @@ mod test {
610
631
mine_locations : HashSet < ( SizeType , SizeType ) > ,
611
632
fields : Vec < Vec < FieldType > > ,
612
633
}
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
619
641
fn create_test_info_5x6 ( ) -> TestInfo {
620
642
let fields = vec ! [
621
643
vec![
@@ -791,12 +813,14 @@ mod test {
791
813
}
792
814
793
815
#[ 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
800
824
fn get_value_general_test ( ) {
801
825
let width = 6 ;
802
826
let height = 6 ;
@@ -1355,5 +1379,99 @@ mod test {
1355
1379
check_indices ( -1 , -1 , "Negative both" ) ;
1356
1380
}
1357
1381
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
+
1358
1476
// TODO Write test to full game
1359
1477
}
0 commit comments