// // Vier-op-een-rij in C++ // // Walter Kosters, Informatica, Universiteit Leiden // kosters@liacs.nl // 21.11.2006 // // Verander zelf de gedeeltes tussen TODObegin en TODOeind // #include #include // unistd zorgt ervoor dat sleep ( ) gebruikt kan worden #include // cstdlib zorgt ervoor dat we random getallen kunnen gebruiken #include // ctime zorgt ervoor dat we de tijd kunnen gebruiken #include "Xsowl.h" using namespace std; const int keus1 = 1; const int keus2 = 2; const int keus3 = 3; const int keus4 = 4; const int keus5 = 5; const int keus6 = 6; const int keus7 = 7; const int herteken = 400; const int ikzet = 17; const int recursie = 100; const int willekeurig = 123; const int stopermee = 42; const int m = 6; const int n = 7; const int VIER = 4; const int DIEPTE = 8; const int breedtescherm = 500; const int hoogtescherm = 500; // verander kleur red in green, en andersom void wisselkleur (unsigned long & kleur) { if ( kleur == red ) kleur = green; else kleur = red; }//wisselkleur //============================================================================ class bord { public: unsigned long inhoud[m][n]; unsigned long kleur; unsigned long anderekleur; bool kolommen[n]; bord ( ); bool magzet (int rij, int kolom); void doezet (int rij, int kolom); void ontdoezet (int rij, int kolom); int zoekrij (int kolom); void werp (int kolom); bool toegestaan (int kolom); bool bordvol ( ); unsigned long gewonnen ( ); int randomzet ( ); int beperkterandomzet ( ); unsigned long slimmezet (int & beste, int diepte); int recursiezet ( ); void evalueer (int & twee, int & drie, int & vier, unsigned long kl); int als (int kolom, unsigned long kl); int ikdoezet ( ); };//bord // constructor: maakt leeg bord; kleur red begint bord::bord ( ) { int rij, kolom; for ( rij = 0; rij < m; rij++ ) for ( kolom = 0; kolom < n; kolom++ ) inhoud[rij][kolom] = black; kleur = red; anderekleur = green; for ( kolom = 0; kolom < n; kolom++ ) kolommen[kolom] = true; }//bord::bord // mag zet op plek (rij,kolom)? bool bord::magzet (int rij, int kolom) { return ( 0 <= rij && rij < m && 0 <= kolom && kolom < n && inhoud[rij][kolom] == black ); }//void bord::magzet // doe zet op plek (rij,kolom) void bord::doezet (int rij, int kolom) { inhoud[rij][kolom] = kleur; wisselkleur (kleur); wisselkleur (anderekleur); }//bord::doezet // ontdoe zet op plek (rij,kolom) void bord::ontdoezet (int rij, int kolom) { inhoud[rij][kolom] = black; wisselkleur (kleur); wisselkleur (anderekleur); }//bord::ontdoezet // zoek van boven af laagste lege rij in kolom // als kolom vol: retourneer m int bord::zoekrij (int kolom) { int rij = m; if ( kolom < 0 || kolom >= n ) return m; while ( rij > 0 && inhoud[rij-1][kolom] == black ) rij--; return rij; }//bord::zoekrij // gooi in kolom, aannemend dat het mag void bord::werp (int kolom) { int rij = zoekrij (kolom); doezet (rij, kolom); }//bord::werp // mag je in kolom zetten? bool bord::toegestaan (int kolom) { return ( zoekrij (kolom) != m ); }//bord::toegestaan // is het bord vol? bool bord::bordvol ( ) { int kolom; for ( kolom = 0; kolom < n; kolom++ ) if ( inhoud[m-1][kolom] == black ) return false; return true; }//bord::bordvol // heeft iemand gewonnen? unsigned long bord::gewonnen ( ) { int i, j, k, l, tel; unsigned long kl; for ( i = 0; i < m; i++ ) for (j = 0; j < n; j++ ) if ( inhoud[i][j] != black ) { kl = inhoud[i][j]; tel = 0; k = i; l = j; while ( k < m && inhoud[k][l] == kl ) { tel++; k++; }//while if ( tel >= VIER ) return kl; tel = 0; k = i; l = j; while ( l < n && inhoud[k][l] == kl ) { tel++; l++; }//while if ( tel >= VIER ) return kl; tel = 0; k = i; l = j; while ( k < m && l < n && inhoud[k][l] == kl ) { tel++; k++; l++; }//while if ( tel >= VIER ) return kl; tel = 0; k = i; l = j; while ( k >= 0 && l < n && inhoud[k][l] == kl ) { tel++; k--; l++; }//while if ( tel >= VIER ) return kl; }//if return black; }//bord::gewonnen // laat de computer een willekeurige (random) zet bepalen // sommige kolommen zijn in eerste instantie verboden int bord::beperkterandomzet ( ) { int kolom; bool okee = false; // eerst checken of er nog een ok kolom is for ( kolom = 0; kolom < n; kolom++ ) if ( kolommen[kolom] && toegestaan (kolom) ) okee = true; if ( ! okee ) for ( kolom = 0; kolom < n; kolom++ ) kolommen[kolom] = true; do { kolom = rand ( ) % n; } while ( ! toegestaan (kolom) || ! kolommen[kolom] ); return kolom; }//void bord::beperkterandomzet // laat de computer een willekeurige (random) zet bepalen, // aannemende dat er nog plek is int bord::randomzet ( ) { int kolom; do { kolom = rand ( ) % n; } while ( ! toegestaan (kolom) ); return kolom; }//void bord::randomzet // voor wie is de stand het beste? beste zet komt terug // recursief, tot en met diepte unsigned long bord::slimmezet (int & beste, int diepte) { int rij, kolom, welke; unsigned long resultaat = black; unsigned long alklaar = gewonnen ( ); beste = -1; if ( alklaar != black ) return alklaar; else if ( diepte == 0 || bordvol ( ) ) return black; else { for ( kolom = 0; kolom < n; kolom++ ) { rij = zoekrij (kolom); if ( rij != m ) { doezet (rij, kolom); resultaat = slimmezet (welke, diepte-1); ontdoezet (rij, kolom); if ( resultaat == kleur ) { beste = kolom; return kleur; }//if else if ( resultaat == black ) beste = kolom; else if ( diepte == DIEPTE ) kolommen[kolom] = false; }//if }//for if ( beste == -1 ) { if ( kleur == red ) return green; else return red; }//if }//else return black; }//bord::slimmezet // laat de computer recursief een zet bedenken int bord::recursiezet ( ) { int beste; unsigned long kl; int j; for ( j = 0; j < n; j++ ) kolommen[j] = true; kl = slimmezet (beste, DIEPTE); if ( kl == kleur ) return beste; else return beperkterandomzet ( ); }//bord::recuriezet // hoe staan we ervoor? void bord::evalueer (int & twee, int & drie, int & vier, unsigned long kl) { int i, j, k, l, tel; twee = 0; drie = 0; vier = 0; for ( i = 0; i < m; i++ ) for (j = 0; j < n; j++ ) if ( inhoud[i][j] == kl ) { tel = 0; k = i; l = j; while ( k < m && inhoud[k][l] == kl ) { tel++; k++; }//while if ( tel >= 4 ) vier++; else if ( tel >= 3 ) drie++; else if ( tel >= 2 ) twee++; tel = 0; k = i; l = j; while ( l < n && inhoud[k][l] == kl ) { tel++; l++; }//while if ( tel >= 4 ) vier++; else if ( tel >= 3 ) drie++; else if ( tel >= 2 ) twee++; tel = 0; k = i; l = j; while ( k < m && l < n && inhoud[k][l] == kl ) { tel++; k++; l++; }//while if ( tel >= 4 ) vier++; else if ( tel >= 3 ) drie++; else if ( tel >= 2 ) twee++; tel = 0; k = i; l = j; while ( k >= 0 && l < n && inhoud[k][l] == kl ) { tel++; k--; l++; }//while if ( tel >= 4 ) vier++; else if ( tel >= 3 ) drie++; else if ( tel >= 2 ) twee++; }//if }//bord::evalueer // wat levert door kleur kl in kolom zetten op? int bord::als (int kolom, unsigned long kl) { int rij, twee, drie, vier; if ( toegestaan (kolom) ) { rij = zoekrij (kolom); inhoud[rij][kolom] = kl; evalueer (twee, drie, vier, kl); inhoud[rij][kolom] = black; //TODObegin// return 5*twee + 50*drie + 1000*vier; //TODOeind// }//if else return -1; }//bord::als // bedenk zelf een zet!!! int bord::ikdoezet ( ) { int beste = -1; int kolom; int bestewaarde = -1, waarde; // welke kolom geeft de "beste" waarde? for ( kolom = 0; kolom < n; kolom++ ) { waarde = als (kolom, kleur); if ( waarde > bestewaarde ) { bestewaarde = waarde; beste = kolom; }//if }//for //TODObegin// // gebruik anderekleur //TODOeind// if ( toegestaan (beste) ) // gelukt? return beste; else return randomzet ( ); }//bord::ikdoezet //============================================================================ // teken kruisje in venster op plek (rij,kolom) in kleur void zetkruis (TWindow & venster, int rij, int kolom, unsigned long kleur) { venster.SetDrawForeground (kleur); venster.DrawLine (36 + kolom * 50, 59 + (m - rij) * 50, 36 + kolom * 50 + 30, 59 + (m - rij) * 50 + 30); venster.DrawLine (36 + kolom * 50, 59 + (m - rij) * 50 + 30, 36 + kolom * 50 + 30, 59 + (m - rij) * 50); }//zetkruis // teken cirkeltje in venster op plek (rij,kolom) in kleur void zetcirkel (TWindow & venster, int rij, int kolom, unsigned long kleur) { venster.SetDrawForeground (kleur); venster.DrawArc (35 + kolom * 50, 60 + (m - rij) * 50, 32, 32, 0, 360); }//zetcirkel // teken juiste figuur in venster op (rij,kolom) in kleur void zetstuk (TWindow & venster, int rij, int kolom, unsigned long kleur) { // denk aan +1 omdat arrays met 0 beginnen te nummeren if ( kleur == red ) zetkruis (venster, rij+1, kolom+1, kleur); else zetcirkel (venster, rij+1, kolom+1, kleur); }//zetstuk // teken in venster bord b in kleur, en zet kruisjes en rondjes void tekenbord (TWindow & venster, unsigned long kleur, bord & b) { int i, j; int rij, kolom; venster.SetDrawForeground (kleur); for ( i = 0; i <= m; i++ ) venster.DrawLine (75, 50 + i * 50, 425, 50 + i * 50); for ( j = 0; j <= n; j++ ) venster.DrawLine (75 + j * 50, 50, 75 + j * 50, 350); for ( rij = 0; rij < m; rij++ ) for ( kolom = 0; kolom < n; kolom++ ) if ( b.inhoud[rij][kolom] != black ) zetstuk (venster, rij, kolom, b.inhoud[rij][kolom]); }//tekenbord //============================================================================ // het hoofdprogramma int Xmain ( ) { int nummer = 0; // wat is er geklikt? bord b; // het speelbord int kolom; // welke kolom? bool klaar = false; // heeft iemand al gewonnen? // initialiseer random-generator srand (time (0)); // maak een venster en toon het TWindow venster (breedtescherm,hoogtescherm); // zeven knoppen met "boodschap" erop. // CheckForEvent geeft bijvorbeeld '1' (= keus1) // terug als erop gedrukt wordt TButton een (85, 370, 30, 30, (char*)" 1 ", keus1, &venster); TButton twee (135, 370, 30, 30, (char*)" 2 ", keus2, &venster); TButton drie (185, 370, 30, 30, (char*)" 3 ", keus3, &venster); TButton vier (235, 370, 30, 30, (char*)" 4 ", keus4, &venster); TButton vijf (285, 370, 30, 30, (char*)" 5 ", keus5, &venster); TButton zes (335, 370, 30, 30, (char*)" 6 ", keus6, &venster); TButton zeven (385, 370, 30, 30, (char*)" 7 ", keus7, &venster); // en de vier menukeuze's TButton stoppen (85, 420, 60, 30, (char*)" Stop ", stopermee, &venster); TButton ik (160, 420, 60, 30, (char*)" Slim ", ikzet, &venster); TButton recursiezetten (235, 420, 80, 30, (char*)" Recursie ", recursie, &venster); TButton randomzetten (335, 420, 80, 30, (char*)" Random ", willekeurig, &venster); // lijnen worden voortaan getekend als ononderbroken lijn met dikte 3 venster.SetDrawLineStyle (LineStyleSolid, 3); // hertekenen verbeteren venster.AddEvent (EventExpose, herteken); tekenbord (venster, black, b); // nummer is 0 zolang er niet op de knop geklikt is while ( nummer >= 0 && nummer != stopermee && !klaar ) { // kijk of er op een knop geklikt is nummer = venster.WaitForEvent ( ); if ( keus1 <= nummer && nummer <= keus7 ) { // wil in kolom nummer-1 zetten kolom = nummer - 1; if ( b.toegestaan (kolom) ) b.werp (kolom); }//if else if ( nummer == recursie ) { kolom = b.recursiezet ( ); b.werp (kolom); }//if else if ( nummer == willekeurig ) { kolom = b.randomzet ( ); b.werp (kolom); }//if else if ( nummer == ikzet ) { kolom = b.ikdoezet ( ); b.werp (kolom); }//if tekenbord (venster, black, b); klaar = ( b.gewonnen ( ) != black || b.bordvol ( ) ); }//while nummer = venster.CheckForEvent ( ); sleep (1); // even pauzeren if ( b.gewonnen ( ) == red ) cout << "Rood heeft gewonnen!" << endl; else if ( b.gewonnen ( ) == green ) cout << "Groen heeft gewonnen!" << endl; else if ( b.bordvol ( ) ) cout << "Remise!" << endl; else cout << "Tot ziens!" << endl; return 0; }//Xmain