1+ using System . Collections ;
2+ using Codebreaker . GameAPIs . Client . Models ;
3+ using Xunit ;
4+
5+ namespace CodeBreaker . BotWithString . Tests ;
6+
7+ public class StringCodeBreakerAlgorithmsTests
8+ {
9+ [ Theory ]
10+ [ InlineData ( GameType . Game6x4 , 4 ) ]
11+ [ InlineData ( GameType . Game8x5 , 5 ) ]
12+ [ InlineData ( GameType . Game5x5x4 , 4 ) ]
13+ public void SelectPeg_Should_ReturnCorrectPeg ( GameType gameType , int expectedFieldsCount )
14+ {
15+ // Arrange
16+ string [ ] testCodes = gameType switch
17+ {
18+ GameType . Game6x4 => [ "Red" , "Blue" , "Green" , "Yellow" ] ,
19+ GameType . Game8x5 => [ "Red" , "Blue" , "Green" , "Yellow" , "Orange" ] ,
20+ GameType . Game5x5x4 => [ "Red" , "Blue" , "Green" , "Yellow" ] ,
21+ _ => [ "Red" , "Blue" , "Green" , "Yellow" ]
22+ } ;
23+
24+ // Act & Assert
25+ for ( int i = 0 ; i < expectedFieldsCount ; i ++ )
26+ {
27+ string actual = testCodes . SelectPeg ( gameType , i ) ;
28+ Assert . Equal ( testCodes [ i ] , actual ) ;
29+ }
30+ }
31+
32+ [ Theory ]
33+ [ InlineData ( GameType . Game6x4 , 4 ) ]
34+ [ InlineData ( GameType . Game6x4 , - 1 ) ]
35+ [ InlineData ( GameType . Game8x5 , 5 ) ]
36+ [ InlineData ( GameType . Game8x5 , - 1 ) ]
37+ [ InlineData ( GameType . Game5x5x4 , 4 ) ]
38+ [ InlineData ( GameType . Game5x5x4 , - 1 ) ]
39+ public void SelectPeg_Should_ThrowException_ForInvalidPegNumber ( GameType gameType , int invalidPegNumber )
40+ {
41+ // Arrange
42+ string [ ] testCodes = gameType switch
43+ {
44+ GameType . Game6x4 => [ "Red" , "Blue" , "Green" , "Yellow" ] ,
45+ GameType . Game8x5 => [ "Red" , "Blue" , "Green" , "Yellow" , "Orange" ] ,
46+ GameType . Game5x5x4 => [ "Red" , "Blue" , "Green" , "Yellow" ] ,
47+ _ => [ "Red" , "Blue" , "Green" , "Yellow" ]
48+ } ;
49+
50+ // Act & Assert
51+ Assert . Throws < InvalidOperationException > ( ( ) => testCodes . SelectPeg ( gameType , invalidPegNumber ) ) ;
52+ }
53+
54+ [ Fact ]
55+ public void HandleBlackMatches_Should_FilterCorrectly_Game6x4 ( )
56+ {
57+ // Arrange
58+ var possibleValues = new List < string [ ] >
59+ {
60+ new string [ ] { "Red" , "Blue" , "Green" , "Yellow" } , // 4 black matches with selection
61+ new string [ ] { "Red" , "Blue" , "Green" , "Black" } , // 3 black matches with selection
62+ new string [ ] { "Red" , "Blue" , "Black" , "White" } , // 2 black matches with selection
63+ new string [ ] { "Red" , "Black" , "White" , "Orange" } , // 1 black match with selection
64+ new string [ ] { "Black" , "White" , "Orange" , "Purple" } // 0 black matches with selection
65+ } ;
66+ string [ ] selection = new string [ ] { "Red" , "Blue" , "Green" , "Yellow" } ;
67+
68+ // Act
69+ var result = possibleValues . HandleBlackMatches ( GameType . Game6x4 , 4 , selection ) ;
70+
71+ // Assert
72+ Assert . Single ( result ) ;
73+ Assert . Equal ( new string [ ] { "Red" , "Blue" , "Green" , "Yellow" } , result [ 0 ] ) ;
74+ }
75+
76+ [ Fact ]
77+ public void HandleBlackMatches_Should_FilterCorrectly_Game8x5 ( )
78+ {
79+ // Arrange
80+ var possibleValues = new List < string [ ] >
81+ {
82+ new string [ ] { "Red" , "Blue" , "Green" , "Yellow" , "Orange" } , // 5 black matches with selection
83+ new string [ ] { "Red" , "Blue" , "Green" , "Yellow" , "Black" } , // 4 black matches with selection
84+ new string [ ] { "Red" , "Blue" , "Green" , "Black" , "White" } , // 3 black matches with selection
85+ } ;
86+ string [ ] selection = new string [ ] { "Red" , "Blue" , "Green" , "Yellow" , "Orange" } ;
87+
88+ // Act
89+ var result = possibleValues . HandleBlackMatches ( GameType . Game8x5 , 3 , selection ) ;
90+
91+ // Assert
92+ Assert . Single ( result ) ;
93+ Assert . Equal ( new string [ ] { "Red" , "Blue" , "Green" , "Black" , "White" } , result [ 0 ] ) ;
94+ }
95+
96+ [ Fact ]
97+ public void HandleWhiteMatches_Should_FilterCorrectly ( )
98+ {
99+ // Arrange
100+ var possibleValues = new List < string [ ] >
101+ {
102+ new string [ ] { "Blue" , "Red" , "Yellow" , "Green" } , // All colors match but in different positions (4 white matches)
103+ new string [ ] { "Blue" , "Red" , "Green" , "Yellow" } , // 3 colors match in different positions
104+ new string [ ] { "Red" , "Blue" , "Green" , "Yellow" } , // All colors match in same positions (0 white matches)
105+ new string [ ] { "Black" , "White" , "Orange" , "Purple" } // No matching colors
106+ } ;
107+ string [ ] selection = new string [ ] { "Red" , "Blue" , "Green" , "Yellow" } ;
108+
109+ // Act
110+ var result = possibleValues . HandleWhiteMatches ( GameType . Game6x4 , 4 , selection ) ;
111+
112+ // Assert
113+ Assert . Single ( result ) ;
114+ Assert . Equal ( new string [ ] { "Blue" , "Red" , "Yellow" , "Green" } , result [ 0 ] ) ;
115+ }
116+
117+ [ Fact ]
118+ public void HandleNoMatches_Should_RemoveAllWithMatchingColors ( )
119+ {
120+ // Arrange
121+ var possibleValues = new List < string [ ] >
122+ {
123+ new string [ ] { "Red" , "Blue" , "Green" , "Yellow" } , // Contains Red and Blue from selection
124+ new string [ ] { "Black" , "White" , "Orange" , "Purple" } , // No matching colors
125+ new string [ ] { "Red" , "Black" , "White" , "Orange" } , // Contains Red from selection
126+ new string [ ] { "Pink" , "Brown" , "Gray" , "Cyan" } // No matching colors
127+ } ;
128+ string [ ] selection = new string [ ] { "Red" , "Blue" , "Green" , "Yellow" } ;
129+
130+ // Act
131+ var result = possibleValues . HandleNoMatches ( GameType . Game6x4 , selection ) ;
132+
133+ // Assert
134+ Assert . Equal ( 2 , result . Count ) ;
135+ Assert . Contains ( new string [ ] { "Black" , "White" , "Orange" , "Purple" } , result ) ;
136+ Assert . Contains ( new string [ ] { "Pink" , "Brown" , "Gray" , "Cyan" } , result ) ;
137+ }
138+
139+ [ Fact ]
140+ public void HandleBlueMatches_Should_ReturnUnfiltered_ForNonGame5x5x4 ( )
141+ {
142+ // Arrange
143+ var possibleValues = new List < string [ ] >
144+ {
145+ new string [ ] { "Red" , "Blue" , "Green" , "Yellow" } ,
146+ new string [ ] { "Black" , "White" , "Orange" , "Purple" }
147+ } ;
148+ string [ ] selection = new string [ ] { "Red" , "Blue" , "Green" , "Yellow" } ;
149+
150+ // Act
151+ var result6x4 = possibleValues . HandleBlueMatches ( GameType . Game6x4 , 0 , selection ) ;
152+ var result8x5 = possibleValues . HandleBlueMatches ( GameType . Game8x5 , 0 , selection ) ;
153+
154+ // Assert
155+ Assert . Equal ( possibleValues . Count , result6x4 . Count ) ;
156+ Assert . Equal ( possibleValues . Count , result8x5 . Count ) ;
157+ }
158+
159+ [ Fact ]
160+ public void HandleBlueMatches_Should_FilterCorrectly_ForGame5x5x4 ( )
161+ {
162+ // Arrange
163+ var possibleValues = new List < string [ ] >
164+ {
165+ new string [ ] { "RedCircle" , "BlueSquare" , "GreenTriangle" , "YellowStar" } , // Should have some partial matches
166+ new string [ ] { "RedSquare" , "BlueCircle" , "GreenStar" , "YellowTriangle" } , // Should have some partial matches
167+ new string [ ] { "BlackCircle" , "WhiteSquare" , "OrangeTriangle" , "PurpleStar" } // Should have no partial matches
168+ } ;
169+ string [ ] selection = new string [ ] { "RedCircle" , "BlueSquare" , "GreenTriangle" , "YellowStar" } ;
170+
171+ // Act
172+ var result = possibleValues . HandleBlueMatches ( GameType . Game5x5x4 , 1 , selection ) ;
173+
174+ // Assert
175+ // The exact result depends on the partial match logic implementation
176+ Assert . True ( result . Count <= possibleValues . Count ) ;
177+ }
178+
179+ [ Theory ]
180+ [ ClassData ( typeof ( GenerateAllPossibleCombinationsTestData ) ) ]
181+ public void GenerateAllPossibleCombinations_Should_GenerateCorrectCount ( GameType gameType , string [ ] possibleValues , int expectedCount )
182+ {
183+ // Act
184+ var result = StringCodeBreakerAlgorithms . GenerateAllPossibleCombinations ( gameType , possibleValues ) ;
185+
186+ // Assert
187+ Assert . Equal ( expectedCount , result . Count ) ;
188+
189+ // Verify all combinations are unique
190+ var uniqueCount = result . Select ( arr => string . Join ( "," , arr ) ) . Distinct ( ) . Count ( ) ;
191+ Assert . Equal ( expectedCount , uniqueCount ) ;
192+ }
193+
194+ [ Fact ]
195+ public void HandleBlackMatches_Should_ThrowException_ForInvalidHits ( )
196+ {
197+ // Arrange
198+ var possibleValues = new List < string [ ] >
199+ {
200+ new string [ ] { "Red" , "Blue" , "Green" , "Yellow" }
201+ } ;
202+ string [ ] selection = new string [ ] { "Red" , "Blue" , "Green" , "Yellow" } ;
203+
204+ // Act & Assert
205+ Assert . Throws < ArgumentException > ( ( ) => possibleValues . HandleBlackMatches ( GameType . Game6x4 , - 1 , selection ) ) ;
206+ Assert . Throws < ArgumentException > ( ( ) => possibleValues . HandleBlackMatches ( GameType . Game6x4 , 5 , selection ) ) ;
207+ }
208+
209+ [ Fact ]
210+ public void StringPegWithFlag_Should_WorkCorrectly ( )
211+ {
212+ // Arrange
213+ var peg = new StringPegWithFlag ( "Red" , false ) ;
214+
215+ // Act
216+ var usedPeg = peg with { Used = true } ;
217+
218+ // Assert
219+ Assert . Equal ( "Red" , peg . Value ) ;
220+ Assert . False ( peg . Used ) ;
221+ Assert . Equal ( "Red" , usedPeg . Value ) ;
222+ Assert . True ( usedPeg . Used ) ;
223+ }
224+ }
225+
226+ public class GenerateAllPossibleCombinationsTestData : IEnumerable < object [ ] >
227+ {
228+ public IEnumerator < object [ ] > GetEnumerator ( )
229+ {
230+ // Game6x4: 4 positions, 2 colors = 2^4 = 16 combinations
231+ yield return new object [ ] { GameType . Game6x4 , new string [ ] { "Red" , "Blue" } , 16 } ;
232+
233+ // Game6x4: 4 positions, 3 colors = 3^4 = 81 combinations
234+ yield return new object [ ] { GameType . Game6x4 , new string [ ] { "Red" , "Blue" , "Green" } , 81 } ;
235+
236+ // Game8x5: 5 positions, 2 colors = 2^5 = 32 combinations
237+ yield return new object [ ] { GameType . Game8x5 , new string [ ] { "Red" , "Blue" } , 32 } ;
238+
239+ // Game5x5x4: 4 positions, 3 colors = 3^4 = 81 combinations
240+ yield return new object [ ] { GameType . Game5x5x4 , new string [ ] { "Red" , "Blue" , "Green" } , 81 } ;
241+ }
242+
243+ IEnumerator IEnumerable . GetEnumerator ( ) => GetEnumerator ( ) ;
244+ }
0 commit comments