
//
// 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 <iostream>
#include <unistd.h>
// unistd zorgt ervoor dat sleep ( ) gebruikt kan worden
#include <cstdlib>
// cstdlib zorgt ervoor dat we random getallen kunnen gebruiken
#include <ctime>
// 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


