-->

vrijdag 21 april 2017

4 op een rij (met zeer eenvoudige agent)

In een voorgaande blog heb ik al aangegeven dat een AI-agent een combinatie van algoritmen is dat er voor zorgt dat de best mogelijk beslissing wordt genomen gegeven bepaalde onzekerheid in de omgeving waarin de agent acteert.

Vervolgens wou ik in vier stappen een AI agent maken die een waardig tegenstander van een menselijke speler zou worden.

Stap 1. 4 op een rij met twee menselijke spelers.

Stap 2. 4 op een rij waarbij de gebruiker tegen een (zeer simpele) AI-agent speelt. De AI-agent weet enkel wat geldige zetten zijn (kies een kolom tussen 0 en 7 en die niet vol is). Als keuze voor de kolom gebruikt ie een randomize-functie.

Stap 3. Deze agent kent de geldige zetten, maar kijkt ook naar de stand van het bord om zijn keuze te maken.

Stap 4. De agent kijkt niet alleen naar geldige stappen en maakt een inschatting wat de beste volgende stap. Deze agent houdt bij zijn inschatting ook rekening met 2 of meer toekomstige stappen van hem en zijn tegenstander.



In deze versie van 4 op een rij speel je als gebruiker tegen een zeer eenvoudige agent. Eigenlijk kiest de agent alleen maar een willekeurige kolom voor zijn zet. Hij is nog net wel zo intelligent dat ie weet dat alleen geldige zetten zijn toegestaan. M.a.w. zetten tussen 0 en 7 (aantal kolommen in het bord) en dat de kolom niet vol mag zijn.

Bord.java beschrijft het bord en de belangrijkste spelregels.


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package javaapplication10;
import java.util.Scanner;
/**
 *
 * @author gerard
 */


class Board{
    byte[][] board = new byte[6][7];
    
    public Board(){
        board = new byte[][]{
            {0,0,0,0,0,0,0,},
            {0,0,0,0,0,0,0,},
            {0,0,0,0,0,0,0,},
            {0,0,0,0,0,0,0,},
            {0,0,0,0,0,0,0,},
            {0,0,0,0,0,0,0,},    
        };
        
     
        
    } 
    
    
    
    public void undoMove(int column){
        for(int i=0;i<=5;++i){
            if(board[i][column] != 0) {
                board[i][column] = 0;
                break;
            }
        }        
    }

}
public class Bord {

    private Board b;
    private Scanner scan;
    private int nextMoveLocation=-1;
    private int maxDepth = 9;
    
          
    private static final char[] symbolen = new char[] { 'X', 'O','.' };
    private final char[][] velden;
    private int breedte;
    private int hoogte;
    
    private int gekozenKolom = -1; // laatst gekozen kolom
    private int grensRij = -1;// hoogste rij (grensrij)bij deze kolom
    
    

    public Bord(int pbreedte, int phoogte) {
        this.velden = new char[phoogte][pbreedte];
        
        for (int rij = 0; rij < this.velden.length; rij++)
          {
        for (int kolom = 0; kolom < this.velden[rij].length; kolom++)
        {
            this.velden[rij][kolom] = symbolen[2];
        }
    } 
    breedte = this.velden[0].length;
    hoogte = this.velden.length;
        
        
    }       
    public String toString() {
        String BordBeeld = "";
        for (int rij = 0; rij < this.hoogte; rij++)
           {
           for (int kolom = 0; kolom < this.breedte; kolom++)
           {
            BordBeeld = BordBeeld + this.velden[rij][kolom];
           }
           BordBeeld = BordBeeld + "\n";
           }
       return BordBeeld; 
    }
    
   
     public boolean isLegaleZet(int pkolom){
            boolean result = true;
                        
            if ( !(0 <= pkolom && pkolom < breedte)) {
                System.out.println("Kolom moeten tussen 0 and " +
                                   (this.hoogte - 1) + " zijn");
                result = false;
            }
           
         return result;
    }
     
     public boolean KolomIsVol(int pkolom) {
          boolean result = true;
          int hoogte = 0;// this.velden.length - 1;
          
          if (this.velden[hoogte][pkolom] != symbolen[2]) {
             System.out.println("Deze Kolom is vol.");
             result = false; 
          }
       
           return result;
         
     }
    
     public boolean BordIsVol() {
         boolean result = true;
         int aantalVolleKol = 0;
         
          for (int kol = 0;kol <= this.breedte -1; kol++) {
              if (KolomIsVol(kol)) { 
                  aantalVolleKol++;
              }
          }   
         if (aantalVolleKol < this.breedte) {
             result = false;
         }
       return result;  
     }
         
           
     
    //Placing a Move on the board
    public void DoeZet(int column, int player){ 
                   
            for (int h = this.hoogte -1; h>=0; h--) {
                  if (this.velden[h][column] == '.') {// er is nog ruimte
                   
                    
                    this.gekozenKolom = column;
                    this.grensRij = h;
                    this.velden[this.grensRij][this.gekozenKolom] = symbolen[player]; 
                    break;
                    }    
        
            }
      }
    
    public boolean WinnendeZet() {
     
        char sym = this.velden[this.grensRij][this.gekozenKolom];
        String reeks = String.format("%c%c%c%c", sym, sym, sym, sym);
        return bevat(this.horizontaal(), reeks) ||
               bevat(this.vertikaal(), reeks) ||
               bevat(this.slashDiagonaal(), reeks) ||
               bevat(this.backslashDiagonaal(), reeks);
    }

    /**
     * zit er een winnende reeks in de horizontale rij
     */
    private String horizontaal() {
        return new String(this.velden[this.grensRij]);
    }

    /**
     * zit er een winende reeks en ide verticale rij
     */
    private String vertikaal() {
        StringBuilder sb = new StringBuilder(this.hoogte);
        for (int h = 0; h < this.hoogte; h++) {
            sb.append(this.velden[h][this.gekozenKolom]);
        }
        return sb.toString();
    }

    /**
     *  
     * 
     */
    private String slashDiagonaal() {
        StringBuilder sb = new StringBuilder(this.hoogte);
        for (int h = 0; h < this.hoogte; h++) {
            int w = this.gekozenKolom + this.grensRij - h;
            if (0 <= w && w < this.breedte) {
                sb.append(this.velden[h][w]);
            }
        }
        return sb.toString();
    }

    /**
     *
     */
    private String backslashDiagonaal() {
        StringBuilder sb = new StringBuilder(this.hoogte);
        for (int h = 0; h < this.hoogte; h++) {
            int w = this.gekozenKolom - this.grensRij + h;
            if (0 <= w && w < this.breedte) {
                sb.append(this.velden[h][w]);
            }
        }
        return sb.toString();
    }

    private boolean bevat(String hooiberg, String naald) {
        return hooiberg.contains(naald);
    }
    
              
    }    
    

spel.java handelt de interactie tussen speler (gebruiker) en agent af.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package javaapplication10;

import java.util.Random;
import java.util.Scanner;

/**
 *
 * @author gerard
 */
public class Spel {
    Bord b = new Bord(7,8);
    int zet;
     
    public int GebruikerKiestZet(){
        System.out.println("Your move (0-6): ");
         Scanner scan = new Scanner(System.in);
        int g_zet = scan.nextInt();
        while (!b.isLegaleZet(g_zet) || !b.KolomIsVol(g_zet)){
             g_zet = scan.nextInt();
        }
        return g_zet;
    }
    
    public int AgentKiestZet() {
        
        int max = 7; // dit moet nog geparametiseerd worden
        int min =0;
        Random random = new Random();
        int l_zet = random.nextInt(max - min + 1) + min;
        
         while (!b.isLegaleZet(l_zet) || !b.KolomIsVol(l_zet)){
             l_zet = random.nextInt(max - min + 1) + min;
        }
        
       return l_zet; 
    }
    
    public int speel() {
          System.out.println(b);
          
          // remise moet nog worden ingebouwd. 
        while(true){ 
            zet = GebruikerKiestZet();
            b.DoeZet(zet, 0);
            System.out.println(b);
            if (b.WinnendeZet()) {System.out.println("You Win!");break;}
                
                      
            zet = AgentKiestZet();
            b.DoeZet(zet, 1);
            System.out.println(b);
            if (b.WinnendeZet()) {System.out.println("Agent wins!");break;}
            
    
        }
        return 1;
        
    }     
        
     
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
               
        Spel spel = new Spel();
        spel.speel();
        
        // ingebouwd moe tworden wie begint agent of gebruiker
       // Scanner scan = new Scanner(System.in);
       // System.out.println("Would you like to play first? (yes/no) ");
        //String answer = scan.next().trim();
        
      // if(answer.equalsIgnoreCase("yes")) GebruikerKiestZet();
      //  b.displayBoard();
      //  b.placeMove(3, 1);
       // b.displayBoard(); 
        
}
}

woensdag 12 april 2017

Eerste Orde logica

Introductie


Eerste orde logica is een uitbreiding van de predicaten logica. Je kunt er meer soorten zinnen in kwijt.

Bij propsitionele logica spreken we over relaties tussen proposities (uitspraken die in hun geheel waar of onwaar zijn), we spreken over de relaties tussen de verschillende relaties tussen de proposities en we hebben diverse mechanismen om logische conclusies te trekken op basis van die relaties tussen proposities.

Bijvoorbeeld:
zin p: Jack kent Jille
zin q: Jill kent Jack

premisse: Jack kent Jill (p)
premisse: als Jack kent Jill dan Jill kent Jack(p -> q)
Implication elimation: Jill kent Jack (q)

Stel nu dat we meer generieke uitspraken willen doen over de wereld. Bijvoorbeeld: "Als een persoon, een ander persoon kent dan kent de tweede persoon de eerste ook" en we weten dat Jack, Jill kent.

Eerste orde Logica is een uitbreding van predicaten logica om dit soort zinnen uit te drukken in een formele taal en op basis daarvan logsche conclusies te maken.

Syntax



 In Relational Logic, there are no propositional constants; instead we have object constants, relation constants(predicate), and variables en Functies.

Let op functies en relaties lijken wat vorm erg op elkaar p(a,a) en q(a,b), maar een relatie geeft waar of niet waar en een functes geeft op basis van de parameters een objectconstante terug.

There are three types of sentences in Relational Logic, viz. relational sentences (the analog of propositions in Propositional Logic), logical sentences (analogous to the logical sentences in Propositional Logic), and quantified sentences

 

Negation:p(a))
Conjunction:(p(a) ∧ q(b, c))
Disjunction:(p(a) ∨ q(b, c))
Implication:(p(a) ⇒ q(b, c))
Biconditional:(p(a) ⇔ q(b, c))

A universally quantified sentence is used to assert that all objects have a certain property.

(∀x.(p(x) ⇒ q(x,x)))

An existentially quantified sentence is used to assert that some object has a certain property.

(∃x.(p(x) ∧ q(x,x)))

 Note that quantified sentences can be nested within other sentences.

x.p(x) ⇒ q(x)     en ∀x.(p(x) ⇒ q(x)) zijn verschillend
 
Precedence:  In Relational Logic, the precedence relations of the logical operators are the same as in Propositional Logic, and quantifiers have higher precedence than logical operators.


Eigenschappen van gekwantificeerde zinnnen:(hebben we strtaks nodig bij vaststellen demantiek)

An expression in Relational Logic is ground if and only if it contains no variables. For example, the sentence p(a) is ground, whereas the sentence ∀x.p(x) is not.

An occurrence of a variable is free if and only if it is not in the scope of a quantifier of that variable

For example, y is free and x is bound in the following sentence.
x.q(x,y)


A sentence is open if and only if it has free variables.


Semantiek


The Herbrand base for a vocabulary is the set of all ground relational sentences that can be formed from the constants of the language. Said another way, it is the set of all sentences of the form r(t1,...,tn), where r is an n-ary relation constant and t1, ... , tn are object constants.

For a vocabulary with object constants a and b and relation constants p and q where p has arity 1 and q has arity 2, the Herbrand base is shown below.
{p(a), p(b), q(a,a), q(a,b), q(b,a), q(b,b)}


A truth assignment for a Relational Logic language is a function that maps each ground relational sentence in the Herbrand base to a truth value.


p(a)1
p(b)0
q(a,a)1
q(a,b)0
q(b,a)1
q(b,b)0

Wanneer de Herbrand-base is vastgesteld kun je complexere zinnen maken met de logische connectoren. Net las bij Propositielogica:


Alleen waar P en Q staat, kun je vervangen door p(a) en p(b)

omgaan met kwantifiers

A universally quantified sentence is true for a truth assignment if and only if every instance of the scope of the quantified sentence is true for that assignment.

An existentially quantified sentence is true for a truth assignment if and only if some instance of the scope of the quantified sentence is true for that assignment.


satisfies a sentence with free variables if and only if it satisfies every instance of that sentence. A truth assignment satisfies a set of sentences if and only if it satisfies every sentence in the set.

omgaan met functies

For a vocabulary with a single object constant a and a single unary function constant f and a single unary relation constant r, the Herbrand base consists of the sentences shown below.
{r(a), r(f(a)), r(f(f(a))), ...}

hum: probleem oneindige bas -> hoe gaan we waarheidstabel maken?

Luckily, things are not always so bad. In some cases, only finitely many elements of the Herbrand base are true. In such cases, we can describe a truth assignment in finite space by writing 

 

 Werken met één zin (die kwantifiers heeft)

Evaluatie
Gegeven eerdere interpratatie van de Herbrand-baswat is de volgende zin true of vals

x.(p(x) ⇒ q(x,x))

p(a) ⇒ q(a,a)  
p(b) ⇒ q(b,b)


p(a) ⇒ q(a,a))1
(p(b) ⇒ q(b,b))1

allebei 1 dus

x.(p(x) ⇒ q(x,x)) → 1


Bij een existentiele zin minimaal 1 instantie waar, maakt de hele zin waar.
 

Satisfactie

p(a)

p(b) q(a) q(b) p(a) ∨ p(b) x.(p(x) ⇒ q(x)) x.q(x)
1 1 1 1 1 1 1
1 1 1 0 1 0 1
1 1 0 1 1 0 1
1 1 0 0 1 0 0
1 0 1 1 1 1 1
1 0 1 0 1 1 1
1 0 0 1 1 0 1
1 0 0 0 1 0 0
0 1 1 1 1 1 1
0 1 1 0 1 0 1
0 1 0 1 1 1 1
0 1 0 0 1 0 0
0 0 1 1 0 1 1
0 0 1 0 0 1 1
0 0 0 1 0 1 1
0 0 0 0 0 1 0
Looking at the table, we see that there are twelve truth assignments that make the first sentence true, nine that make the second sentence true, twelve that make the third sentence true, and five that make them all true (rows 1, 5, 6, 9, and 11).

omgaan met functies:
The good news is that, even though evaluation and satisfaction are not directly computable, there are effective procedures for indirectly determining validity, contingency, unsatisfiability, logical entailment, and so forth that work in many cases even when our usual direct methods fail.



Werken met 2 of meer zinnen (entailment)

nader uit te werken






 

maandag 10 april 2017

4 op een rij (twee menselijke spelers)

Introductie

Een AI-agent is een combinatie van algoritmen dat er voor zorgt dat de best mogelijk beslissing wordt genomen gegeven bepaalde onzekerheid in de omgeving waarin de agent acteert.

Vier op een rij is een voorbeeld van het vinden van een oplossing in een adversarial-omgeving. M.a.w. een omgeving met een tegenspeler. Een veel gebruikt algoritme om in deze omgeving om de beste beslissing te nemen is het minmax-algoritme.

Uiteindelijk is het best een ingewikkeld programma om een goede 4 op een rij speler te programmeren. Daarom ga ik het programma stapsgewijs opzetten:

Stap 1. 4 op een rij met twee menselijke spelers. Dit lijkt mij de meest eenvoudige versie om te maken

Stap 2. 4 op een rij waarbij de gebruiker tegen een (zeer simpele) AI-agent speelt. De AI-agent weet enkel wat geldige zetten zijn (kies een kolom tussen 0 en 7 en die niet vol is). Als keuze voor de kolom gebruikt ie een randomize-functie.

Stap 3. 4 op een rij waarbij de gebruiker tegen een iets intelligentere agent speel. Deze agent kent ook de geldige zetten, maar kijkt ook naar de stand van het bord om zijn keuze te maken. Hierbij zijn twee varianten te maken. Een agent die alleen kijkt of hij bij de volgende stap 2,3, op 4 op een rij heeft of een agent die ook kijkt of hij kan voorkomen dat de gebruiker 4 op een rij krijgt.

Stap 4. De agent kijkt niet alleen naar geldige stappen en maakt een inschatting wat de beste volgende stap. Deze agent houdt bij zijn inschatting ook rekeing met 2 of meer toekomstige stappen van hem en zijn tegenstander.  Dit is in feite het minmax algoritme  in combinatie met een utitliteitsfunctie. Meestal worden deze agenten pas echt als enigzins intelligent gezien. Vooral als agenten meer dan twee of drie stappen vooruit kunnen redeneren. Stap 4 lijkt een recursieve versie van stap drie te zijn. 

Spelregels

  1. Vier op een rij wordt gespeeld op een (verticaal) bord van 8x6 vakken
  2. Er zijn twee spelers
  3. De spelers spelen om de beurt
  4. Een beurt bestaat uit het vullen van een vak met het symbool van de betreffende speler (rood, geel: hier kruisje, rondje)
  5. Een vak kan alleen gevuld worden als het op de bodem van het veld is of als het onderliggende vak gevuld is.
  6. Degene die als een eerste een reeks van vier dezelfde symbolen heeft horizontaal, verticaal of diagonaal, heeft gewonnen.

Stap 1: Java programma (twee menselijke spelers)


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package javaapplication9;
import java.util.Scanner;

/**
 *
 * @author gerard
 */

public class VierOpEenRij {
    private static final char[] spelers = new char[] { 'X', 'O' };

    private final int breedte, hoogte;
    private final char[][] bord;
    private int gekozenKolom = -1; // laatst gekozen kolom
    private int grensRij = -1;// hoogste rij (grensrij)bij deze kolom

    public VierOpEenRij(int pbreedte, int phoogte) {
        this.breedte = pbreedte;
        this.hoogte = phoogte;
        this.bord = new char[phoogte][pbreedte];
        char aChar = '.';

        for (int rij = 0; rij < this.bord.length; rij++)
          {
        for (int kolom = 0; kolom < this.bord[rij].length; kolom++)
        {
            this.bord[rij][kolom] = aChar;
        }
    }   
}   
        
    public String toString() {
        String BordBeeld = "";
        for (int rij = 0; rij < this.bord.length; rij++)
           {
           for (int kolom = 0; kolom < this.bord[rij].length; kolom++)
           {
            BordBeeld = BordBeeld + this.bord[rij][kolom];
           }
           BordBeeld = BordBeeld + "\n";
           }
       return BordBeeld; 
       
     }

    /**
     * geeft speler een keus tot een geldige keus is gemaakt
     */ 
    public void KiesKolom(char psymbol) {
        
           Scanner keyboard = new Scanner(System.in);
             
           
        
      do {           
            int kol = keyboard.nextInt();

            if (! (0 <= kol && kol < this.breedte)) {
                System.out.println("Kolom moeten tussen 0 and " +
                                   (this.breedte - 1) + " zijn");
                continue;
            }
            
            for (int h = this.hoogte - 1; h >= 0; h--) {
                               
                if (this.bord[h][kol] == '.') {// er is nog ruimte
                    this.gekozenKolom = kol;
                    this.grensRij = h;
                    this.bord[this.grensRij][this.gekozenKolom] = psymbol;
                    return;
                }
               
            }
             System.out.println("Kolom " + kol + " is vol.");
        } while (true);
    }

    /**
     * bekijk per zet of er een winnende reeks is gemaakt
     */
    public boolean WinnendeZet() {
     
        char sym = this.bord[this.grensRij][this.gekozenKolom];
        String reeks = String.format("%c%c%c%c", sym, sym, sym, sym);
        return bevat(this.horizontaal(), reeks) ||
               bevat(this.vertikaal(), reeks) ||
               bevat(this.slashDiagonaal(), reeks) ||
               bevat(this.backslashDiagonaal(), reeks);
    }

    /**
     * zit er een winnende reeks in de horizontale rij
     */
    private String horizontaal() {
        return new String(this.bord[this.grensRij]);
    }

    /**
     * zit er een winende reeks en ide verticale rij
     */
    private String vertikaal() {
        StringBuilder sb = new StringBuilder(this.hoogte);
        for (int h = 0; h < this.hoogte; h++) {
            sb.append(this.bord[h][this.gekozenKolom]);
        }
        return sb.toString();
    }

    /**
     *  
     * 
     */
    private String slashDiagonaal() {
        StringBuilder sb = new StringBuilder(this.hoogte);
        for (int h = 0; h < this.hoogte; h++) {
            int w = this.gekozenKolom + this.grensRij - h;
            if (0 <= w && w < this.breedte) {
                sb.append(this.bord[h][w]);
            }
        }
        return sb.toString();
    }

    /**
     *
     */
    private String backslashDiagonaal() {
        StringBuilder sb = new StringBuilder(this.hoogte);
        for (int h = 0; h < this.hoogte; h++) {
            int w = this.gekozenKolom - this.grensRij + h;
            if (0 <= w && w < this.breedte) {
                sb.append(this.bord[h][w]);
            }
        }
        return sb.toString();
    }

    private boolean bevat(String hooiberg, String naald) {
        return hooiberg.contains(naald);
    }

    public static void main(String[] args) {
        
        int speler = 0;
        VierOpEenRij bord = new VierOpEenRij(8, 6);
     
       
        System.out.println("Kies tussen 0 en 7 voor je kolom.");
             
            for (int maxzetten=6*8; maxzetten>0; maxzetten--) {
                char symbol = spelers[speler];
                             
                   
                   System.out.println(bord);
                   System.out.print("\nSpeler " + symbol + " aan zet: ");
                           
                   bord.KiesKolom(symbol);
                   
                   
                
                   if (bord.WinnendeZet()) {
                       System.out.println("Speler " + symbol + " wint!");
                      return;
                   }
                
                speler = 1 - speler;
              }  
           
            System.out.println("Remise.");
        }
    }