Dimentica che connetti 4 funzioni. Oltre a essere inutilmente criptico, il controllo per una vittoria in Connect 4 è più complesso rispetto a tac-tac-toe.
In tic-tac-toe ci sono otto righe da controllare: tre righe, tre colonne e due diagonali. La prima cosa che suggerirò è usare un array monodimensionale per la scheda, quindi gli indici di array sono i seguenti ...
+---+---+---+
| 0 | 1 | 2 |
+---+---+---+
| 3 | 4 | 5 |
+---+---+---+
| 6 | 7 | 8 |
+---+---+---+
Il motivo dell'utilizzo di un array bidimensionale sarebbe rendere più facile l'identificazione delle posizioni e più facile il loro loop su di esse. In questo caso, sostengo che poiché la scheda è così piccola, è in realtà più semplice evitare il problema delle due dimensioni. Eviterò anche i loop, almeno per un primo tentativo, poiché la scala del problema è abbastanza piccola da permetterlo. Quindi ...
bool line_is_full (Board* p_board, char p_player, int p1, int p2, int p3)
{
return ( (p_board [p1] == p_player)
&& (p_board [p2] == p_player)
&& (p_board [p3] == p_player));
}
bool board_has_win (Board* p_board, char p_player)
{
return ( line_is_full (p_board, p_player, 0, 1, 2)
|| line_is_full (p_board, p_player, 3, 4, 5)
|| line_is_full (p_board, p_player, 6, 7, 8)
|| line_is_full (p_board, p_player, 0, 3, 6)
|| line_is_full (p_board, p_player, 1, 4, 7)
|| line_is_full (p_board, p_player, 2, 5, 8)
|| line_is_full (p_board, p_player, 0, 4, 8)
|| line_is_full (p_board, p_player, 2, 4, 6));
}
Solo il controllo della vincita di un giocatore è valido perché l'unico giocatore che devi controllare è il giocatore che ha appena effettuato una mossa. Non sto nemmeno controllando un pareggio qui - ti basta contare quante mosse sono state fatte per gestirlo.
Cose ovvie che potrebbero essere migliorate ...
-
Le righe vuote in board_has_win
servono per enfatizzare gruppi di casi simili. Potresti avere un loop sulle tre righe e un loop sulle tre colonne.
-
In alternativa, board_has_win
è un ciclo "non eseguito" su tutte le otto righe. Ci sono diversi modi in cui questo potrebbe essere espresso usando un ciclo. Probabilmente il più sensato è avere una tabella di dati (dando i tre numeri di cella per ciascuna delle otto linee), in modo che tu possa ricorrere direttamente alle otto righe.
-
line_is_full
potrebbe ovviamente essere riscritto come un ciclo sulle tre celle se solo quelle celle fossero passate come array.
-
In alternativa, per ogni riga (dato il modo in cui li ho specificati), (p2-p1) == (p3-p2)
- c'è una distanza costante tra le celle. Anche (p1+p3)/2 == p2
- la posizione della cella centrale è la media delle due celle terminali. Ciò significa che vengono fornite due celle (a patto di sapere quali due) è possibile determinare la prima e la distanza di passo e il ciclo su tutte e tre.
Personalmente ignorerei i punti 3 e 4: avere un ciclo piuttosto che tre casi non vale davvero nemmeno una piccola quantità di complessità extra. Probabilmente mi concentrerei sul punto 2 - quella tabella di numeri di cella per ogni riga potrebbe avere altri usi nel programma.
Ecco un suggerimento che penso di aver visto per la prima volta nei vecchi tempi in cui le riviste di computer stampavano annunci in BASIC. Ciascuna delle linee ha tre celle. Cosa succede se assegni O
il valore 1
e X
il valore 4
(e zero per vuoto)? Considera il totale dei valori per le tre celle in una riga: ecco una tabella ...
0 .... All three cells empty
1 .... One O, two empty cells
2 .... Two Os, one empty cell
3 .... Three Os - O wins
4 .... One X, two empty cells
5 .... One X, one O, one empty cell
6 .... One X, two Os
7 .... Impossible
8 .... Two Xs, one empty cell
9 .... Two Xs, one O
10 .... Impossible
11 .... Impossible
12 .... Three Xs - X wins
13+ ... Impossible
Questo è un tipo di tabella hash del caso speciale. Questo è utile non solo per individuare una vittoria ma anche per un'IA molto semplice.