-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathadversarial AIs in tic-tac-toe and 2048.nb
10028 lines (9644 loc) · 417 KB
/
adversarial AIs in tic-tac-toe and 2048.nb
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(* Content-type: application/vnd.wolfram.mathematica *)
(*** Wolfram Notebook File ***)
(* http://www.wolfram.com/nb *)
(* CreatedBy='WolframDesktop 13.0' *)
(*CacheID: 234*)
(* Internal cache information:
NotebookFileLineBreakTest
NotebookFileLineBreakTest
NotebookDataPosition[ 161, 7]
NotebookDataLength[ 416369, 10019]
NotebookOptionsPosition[ 391246, 9530]
NotebookOutlinePosition[ 392437, 9567]
CellTagsIndexPosition[ 392256, 9559]
WindowFrame->Normal*)
(* Beginning of Notebook Content *)
Notebook[{
Cell[TextData[{
StyleBox[ButtonBox["Emerging strategies of adversarial AIs for Tic-Tac-Toe & \
2048",
BaseStyle->"Hyperlink",
ButtonData->{
URL["https://community.wolfram.com/groups/-/m/t/2456525"], None},
ButtonNote->"https://community.wolfram.com/groups/-/m/t/2456525"],
FontWeight->"Bold"],
"\n",
StyleBox["by ",
FontSlant->"Italic"],
StyleBox[ButtonBox["Wolfram Emerging Leaders Program",
BaseStyle->"Hyperlink",
ButtonData->{
URL["https://community.wolfram.com/web/campadmin"], None},
ButtonNote->"https://community.wolfram.com/web/campadmin"],
FontSlant->"Italic"]
}], "Text",
CellChangeTimes->{{3.853241130684498*^9, 3.8532411769141665`*^9}}],
Cell[CellGroupData[{
Cell["Abstract", "Section",
CellChangeTimes->{{3.8522157087182827`*^9, 3.852215711788609*^9}}],
Cell["\<\
Artificial Intelligences, also known as AIs, are the current coding system on \
the market. They're being used in countless fields for countless purposes, \
but with the emergence of more AIs comes an important question. What would \
happen if two AIs were paired against each other? What strategies would the \
AIs come up with to win against each other? The goal of our project was to \
answer this question. Starting with the simple game of Tic-Tac-Toe, we found \
unique strategies that the AIs developed; we later branched into more complex \
games such as 2048. \
\>", "Text",
CellChangeTimes->{{3.846943185743832*^9, 3.84694345736534*^9}, {
3.8469435744270353`*^9, 3.846943598233404*^9}, {3.848146578197798*^9,
3.848146603017542*^9}, {3.8483340089553347`*^9, 3.848334032910602*^9}},
CellTags->{"Abstract", "TemplateCell"},
CellID->587432573]
}, Open ]],
Cell[CellGroupData[{
Cell["Tic-Tac-Toe: Building an AI", "Section",
CellChangeTimes->{{3.846944491551374*^9, 3.8469445066018353`*^9}},
CellTags->"SectionHeader",
CellID->453852445],
Cell["\<\
We first decided to explore adversarial pair AIs with the simple game of \
Tic-Tac-Toe. The game has been studied many times, and is relatively simple \
to code as well, so it was a great choice to begin our project.\
\>", "Text",
CellChangeTimes->{{3.846944514627893*^9, 3.846944577785581*^9}},
CellTags->"ExampleTopicSentence",
CellID->816667160],
Cell[CellGroupData[{
Cell["Setting up the Game", "Subsection",
CellChangeTimes->{{3.846944590400711*^9, 3.846944604620426*^9}},
CellLabel->"(Debug) In[32]:=",
CellID->1665523621],
Cell["\<\
Before we could build an AI, we had to set up code to play the game. We \
intended to use 0's for spots that weren't filled, 1's for spots that were \
filled by player 1, and 2's for spots that were filled by player 2. We \
started off with a 2D table to keep track of the board's current state. \
\>", "Text",
CellChangeTimes->{{3.8469446061536417`*^9, 3.84694463815648*^9}, {
3.846944669409569*^9, 3.846944698771317*^9}, {3.84694499993055*^9,
3.846945042638376*^9}},
CellID->2121022790],
Cell["startingboard kept track of the game board's current state:", "CodeText",
CellChangeTimes->{{3.846944702938383*^9, 3.846944735319264*^9}},
CellID->1848786286],
Cell[BoxData[
RowBox[{
RowBox[{"startingboard", " ", "=", " ",
RowBox[{"Table", "[",
RowBox[{
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "0"}], "}"}], ",", " ", "3"}], "]"}]}],
";"}]], "Code",
CellChangeTimes->{{3.846944747252995*^9, 3.846944777439601*^9}},
CellLabel->"In[1]:=",
CellID->951509299],
Cell["\<\
We then set up functions to look at the board and figure out which player \
won. To do this, we set up functions to check the columns, rows, and \
diagonals, then combined it all in one function.\
\>", "Text",
CellChangeTimes->{{3.8469447835722237`*^9, 3.8469448721753674`*^9}},
CellID->64231012],
Cell["Column, Row, and Diagonal functions:", "CodeText",
CellChangeTimes->{{3.8469448854222803`*^9, 3.846944897504917*^9}},
CellID->143754574],
Cell[BoxData[{
RowBox[{
RowBox[{"column", "[", "list_", "]"}], ":=",
RowBox[{"Table", "[",
RowBox[{
RowBox[{"Equal", "@@",
RowBox[{"list", "[",
RowBox[{"[",
RowBox[{"All", ",", "column"}], "]"}], "]"}]}], ",",
RowBox[{"{",
RowBox[{"column", ",", "1", ",", "3"}], "}"}]}], "]"}]}], "\n",
RowBox[{
RowBox[{"row", "[", "list_", "]"}], ":=",
RowBox[{"Table", "[",
RowBox[{
RowBox[{"Equal", "@@",
RowBox[{"list", "[",
RowBox[{"[",
RowBox[{"row", ",", "All"}], "]"}], "]"}]}], ",",
RowBox[{"{",
RowBox[{"row", ",", "1", ",", "3"}], "}"}]}], "]"}]}], "\n",
RowBox[{
RowBox[{"diagonal", "[", "list_", "]"}], ":=",
RowBox[{"{",
RowBox[{
RowBox[{"Equal", "@@",
RowBox[{"{",
RowBox[{
RowBox[{"list", "[",
RowBox[{"[",
RowBox[{"1", ",", "1"}], "]"}], "]"}], ",",
RowBox[{"list", "[",
RowBox[{"[",
RowBox[{"2", ",", "2"}], "]"}], "]"}], ",",
RowBox[{"list", "[",
RowBox[{"[",
RowBox[{"3", ",", "3"}], "]"}], "]"}]}], "}"}]}], ",",
RowBox[{"Equal", "@@",
RowBox[{"{",
RowBox[{
RowBox[{"list", "[",
RowBox[{"[",
RowBox[{"1", ",", "3"}], "]"}], "]"}], ",",
RowBox[{"list", "[",
RowBox[{"[",
RowBox[{"2", ",", "2"}], "]"}], "]"}], ",",
RowBox[{"list", "[",
RowBox[{"[",
RowBox[{"3", ",", "1"}], "]"}], "]"}]}], "}"}]}]}], "}"}]}]}], "Code",\
CellChangeTimes->{{3.8469449028102217`*^9, 3.846944924911924*^9}},
CellLabel->"In[2]:=",
CellID->905364578],
Cell["\<\
Our final win function, called actualWinner, used the column, row, and \
diagonal check functions to see who won. If the function returned 0, that \
meant neither player won and the game should continue.\
\>", "Text",
CellChangeTimes->{{3.846944930056148*^9, 3.846944992603656*^9}, {
3.846945124263245*^9, 3.846945146274094*^9}, {3.848699099757493*^9,
3.848699100806676*^9}},
CellID->1528857719],
Cell["Function to check for the final winner:", "CodeText",
CellChangeTimes->{{3.846945150970191*^9, 3.846945161960495*^9}},
CellID->599186372],
Cell[BoxData[{
RowBox[{
RowBox[{
RowBox[{"winnerRCD", "[", "list_", "]"}], ":=",
RowBox[{"First", "[",
RowBox[{"First", "[",
RowBox[{
RowBox[{"Position", "[",
RowBox[{
RowBox[{"Flatten", "@",
RowBox[{"{",
RowBox[{
RowBox[{"column", "[", "list", "]"}], ",",
RowBox[{"row", "[", "list", "]"}], ",",
RowBox[{"diagonal", "[", "list", "]"}]}], "}"}]}], ",", "True"}],
"]"}], ",",
RowBox[{"{", "9", "}"}]}], "]"}], "]"}]}], "\n"}], "\n",
RowBox[{
RowBox[{"actualWinner", "[", "list_", "]"}], ":=",
RowBox[{"Which", "[",
RowBox[{
RowBox[{
RowBox[{"winnerRCD", "[", "list", "]"}], "\[LessEqual]", "3"}], ",",
RowBox[{"list", "[",
RowBox[{"[",
RowBox[{"1", ",",
RowBox[{"winnerRCD", "[", "list", "]"}]}], "]"}], "]"}], ",",
"\[IndentingNewLine]",
RowBox[{
RowBox[{"winnerRCD", "[", "list", "]"}], "\[LessEqual]", "6"}], ",", " ",
RowBox[{"list", "[",
RowBox[{"[",
RowBox[{
RowBox[{"Floor", "[",
RowBox[{
RowBox[{"winnerRCD", "[", "list", "]"}], "/", "3"}], "]"}], ",",
"1"}], "]"}], "]"}], ",",
RowBox[{
RowBox[{"winnerRCD", "[", "list", "]"}], "\[LessEqual]", "8"}], ",",
RowBox[{"list", "[",
RowBox[{"[",
RowBox[{"2", ",", "2"}], "]"}], "]"}], ",", "True", ",", "0"}],
"]"}]}]}], "Code",
CellChangeTimes->{{3.84694517210121*^9, 3.846945179423843*^9}, {
3.846945396618846*^9, 3.846945400824391*^9}},
CellLabel->"In[5]:=",
CellID->1526008277],
Cell["\<\
We then created a function that checked if all the spots on the board were \
filled. Combined with the win function, this checked for draws, where neither \
player won.\
\>", "Text",
CellChangeTimes->{{3.846945199494933*^9, 3.846945259288681*^9}},
CellID->1881406668],
Cell[BoxData[
RowBox[{
RowBox[{"gameOver2", "[", "tictactoe_", "]"}], ":=",
RowBox[{"!",
RowBox[{"MemberQ", "[",
RowBox[{
RowBox[{"Flatten", "[", "tictactoe", "]"}], ",", "0"}],
"]"}]}]}]], "Code",
CellChangeTimes->{3.846945295309639*^9},
CellLabel->"In[7]:=",
CellID->1656111440],
Cell["\<\
Finally, we wrote code that found a random empty space on the board for the \
player to use, and combined it with a function that replaced the spot with \
the player's number, 1 or 2.\
\>", "Text",
CellChangeTimes->{{3.846945298372362*^9, 3.8469453500766363`*^9}},
CellID->1579451548],
Cell["Code to choose an empty spot on the board and \"play\" a turn:", \
"CodeText",
CellChangeTimes->{{3.84694541438301*^9, 3.846945418513825*^9}, {
3.846945449411675*^9, 3.8469454681174097`*^9}},
CellID->86044198],
Cell[BoxData[{
RowBox[{
RowBox[{
RowBox[{"playturnPosition", "[", "List_", "]"}], ":=",
RowBox[{"Module", "[",
RowBox[{
RowBox[{"{",
RowBox[{
RowBox[{"board", "=",
RowBox[{"Flatten", "[", "List", "]"}]}], ",", " ",
RowBox[{"positions", " ", "=", " ",
RowBox[{"Flatten", "[",
RowBox[{"Position", "[",
RowBox[{
RowBox[{"Flatten", "[", "List", "]"}], ",", " ", "0"}], "]"}],
"]"}]}]}], "}"}], ",", " ", "\[IndentingNewLine]",
RowBox[{"If", "[",
RowBox[{
RowBox[{"gameOver2", "[", "List", "]"}], ",", "\"\<Game Over\>\"", ",",
RowBox[{"RandomChoice", "[", "positions", "]"}]}], "]"}]}], "]"}]}],
"\n"}], "\n",
RowBox[{
RowBox[{"simulate1Move", "[",
RowBox[{"boardWhat_", ",", " ", "randomPosition_", ",", "playerNumber_"}],
"]"}], ":=", " ",
RowBox[{"Module", "[",
RowBox[{
RowBox[{"{",
RowBox[{"startingboard", " ", "=", " ", "boardWhat"}], "}"}], ",",
"\[IndentingNewLine]",
RowBox[{
RowBox[{
RowBox[{"startingboard", "[",
RowBox[{"[",
RowBox[{
RowBox[{"(",
RowBox[{
RowBox[{"Floor", "[",
RowBox[{
RowBox[{"(",
RowBox[{"randomPosition", "-", "1"}], ")"}], "/", "3"}], "]"}],
"+", "1"}], ")"}], ",",
RowBox[{"(",
RowBox[{"randomPosition", "-",
RowBox[{"(",
RowBox[{"3", "*",
RowBox[{"(",
RowBox[{"Floor", "[",
RowBox[{
RowBox[{"(",
RowBox[{"randomPosition", "-", "1"}], ")"}], "/", "3"}],
"]"}], ")"}]}], ")"}]}], ")"}]}], "]"}], "]"}], " ", "=",
"playerNumber"}], ";", "\[IndentingNewLine]", "startingboard"}]}],
"]"}]}]}], "Code",
CellChangeTimes->{{3.846945356070458*^9, 3.846945390850819*^9}},
CellLabel->"In[8]:=",
CellID->56096805],
Cell["\<\
For example, given the starting board, the resulting tic-tac-toe board after \
player 1 moves is:\
\>", "Text",
CellChangeTimes->{{3.846945471853195*^9, 3.846945510589231*^9}},
CellID->1466788968],
Cell[CellGroupData[{
Cell[BoxData[
RowBox[{"simulate1Move", "[",
RowBox[{"startingboard", ",",
RowBox[{"playturnPosition", "[", "startingboard", "]"}], ",", "1"}],
"]"}]], "Input",
CellChangeTimes->{{3.846945514261136*^9, 3.846945523959055*^9}, {
3.8469455568408546`*^9, 3.846945558080881*^9}},
CellLabel->"In[16]:=",
CellID->1885885928],
Cell[BoxData[
RowBox[{"{",
RowBox[{
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"0", ",", "1", ",", "0"}], "}"}]}], "}"}]], "Output",
CellChangeTimes->{{3.846945526853573*^9, 3.846945561751089*^9},
3.846953086821369*^9, 3.8481441126294384`*^9, 3.852284140272205*^9},
CellLabel->"Out[16]="]
}, Open ]],
Cell[TextData[{
"where ",
StyleBox["playturnPosition[startingboard]",
FontSlant->"Italic"],
" returns a random empty position for player 1 to move to."
}], "Text",
CellChangeTimes->{{3.846945566199257*^9, 3.846945587595557*^9}},
CellID->1103073532]
}, Open ]],
Cell[CellGroupData[{
Cell["Creating Training Sets", "Subsection",
CellChangeTimes->{{3.846945603843698*^9, 3.846945622584036*^9}},
CellLabel->"(Debug) In[11]:=",
CellID->1653826862],
Cell["\<\
To set up our AIs, they needed training sets to learn from. We built these \
training sets from running thousands of random games and every time someone \
won, we added the list of boards to their dataset. Over time, this \
accumulated into many example games that each player won.\
\>", "Text",
CellChangeTimes->{{3.84694579653722*^9, 3.846945929742566*^9}},
CellID->1877555278],
Cell["We first set up training sets for our two AIs.", "Text",
CellChangeTimes->{{3.846946133753467*^9, 3.846946148816197*^9}},
CellID->1032133852],
Cell["Training Sets for our two AIs:", "CodeText",
CellChangeTimes->{{3.8469461590252447`*^9, 3.846946180527876*^9}},
CellID->756212602],
Cell[BoxData[{
RowBox[{
RowBox[{"AI1", " ", "=", " ",
RowBox[{"{", "}"}]}], ";"}], "\[IndentingNewLine]",
RowBox[{
RowBox[{"AI2", " ", "=", " ",
RowBox[{"{", "}"}]}], ";"}]}], "Code",
CellChangeTimes->{{3.846946186047371*^9, 3.846946193240715*^9}},
CellLabel->"In[10]:=",
CellID->467849115],
Cell["\<\
Before we could fill the training sets, however, we needed to have code that \
could simulate tic-tac-toe games, so we combined all the functions we wrote \
above into one massive function, which played random tic-tac-toe games a \
certain number of times.\
\>", "Text",
CellChangeTimes->{{3.8469456436610603`*^9, 3.846945644387776*^9}, {
3.846945753084176*^9, 3.846945789250742*^9}, {3.846945932435956*^9,
3.846945966127885*^9}, {3.8469462007283*^9, 3.846946203810709*^9}},
CellID->517298584],
Cell["\<\
Function that simulates a given number of random tic-tac-toe games, and adds \
the list of boards to the winning player's dataset:\
\>", "CodeText",
CellChangeTimes->{{3.84694597549856*^9, 3.846946023681916*^9}, {
3.848146921975624*^9, 3.8481469298417635`*^9}},
CellID->1259367048],
Cell[BoxData[
RowBox[{
RowBox[{"runSoManyEveryTimes", "[", "repeat_", "]"}], ":=", " ",
"\[IndentingNewLine]",
RowBox[{"Table", "[", "\[IndentingNewLine]",
RowBox[{
RowBox[{
RowBox[{"startingboard", "=",
RowBox[{"Table", "[",
RowBox[{
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "0"}], "}"}], ",", "3"}], "]"}]}], ";",
"\[IndentingNewLine]",
RowBox[{"firstplayer", " ", "=", " ", "1"}], ";", "\[IndentingNewLine]",
RowBox[{"listOfgrids", " ", "=", " ",
RowBox[{"{", "startingboard", "}"}]}], ";", "\[IndentingNewLine]",
RowBox[{"While", "[",
RowBox[{
RowBox[{
RowBox[{"!",
RowBox[{"gameOver2", "[", "startingboard", "]"}]}], "&&",
RowBox[{"!",
RowBox[{"(",
RowBox[{
RowBox[{"actualWinner", "[", "startingboard", "]"}], "\[NotEqual]",
"0"}], ")"}]}]}], ",", "\[IndentingNewLine]",
RowBox[{
RowBox[{"startingboard", "=",
RowBox[{"simulate1Move", "[",
RowBox[{"startingboard", ",",
RowBox[{"playturnPosition", "[", "startingboard", "]"}], ",",
"firstplayer"}], "]"}]}], ";", "\[IndentingNewLine]",
RowBox[{"listOfgrids", "=",
RowBox[{"Append", "[",
RowBox[{"listOfgrids", ",", "startingboard"}], "]"}]}], ";",
"\[IndentingNewLine]",
RowBox[{"firstplayer", " ", "=", " ",
RowBox[{"If", "[",
RowBox[{
RowBox[{"EvenQ", "[",
RowBox[{"(",
RowBox[{"firstplayer", "+", "1"}], ")"}], "]"}], ",", "2", ",",
"1"}], "]"}]}], ";"}]}], "\[IndentingNewLine]", "]"}], ";",
"\[IndentingNewLine]",
RowBox[{"If", "[",
RowBox[{
RowBox[{
RowBox[{"actualWinner", "[", "startingboard", "]"}], "\[Equal]",
"1"}], ",",
RowBox[{"AI1", " ", "=", " ",
RowBox[{"Append", "[",
RowBox[{"AI1", ",", " ",
RowBox[{
RowBox[{"AssociationThread", "[",
RowBox[{"listOfgrids", ",",
RowBox[{"RotateLeft", "[",
RowBox[{"listOfgrids", ",", "1"}], "]"}]}], "]"}], "[",
RowBox[{"[",
RowBox[{
RowBox[{
RowBox[{"Range", "[",
RowBox[{
RowBox[{"(",
RowBox[{"Length", "@", "listOfgrids"}], ")"}], "/", "2"}],
"]"}], "*", "2"}], "-", "1"}], "]"}], "]"}]}], "]"}]}], ",",
"\n",
RowBox[{"If", "[",
RowBox[{
RowBox[{
RowBox[{"actualWinner", "[", "startingboard", "]"}], "\[Equal]",
"2"}], ",",
RowBox[{
RowBox[{"AI2", " ", "=", " ",
RowBox[{"Append", "[",
RowBox[{"AI2", ",", " ",
RowBox[{
RowBox[{"AssociationThread", "[",
RowBox[{"listOfgrids", ",",
RowBox[{"RotateLeft", "[",
RowBox[{"listOfgrids", ",", "1"}], "]"}]}], "]"}], "[",
RowBox[{"[",
RowBox[{
RowBox[{"Range", "[",
RowBox[{"Floor", "[",
RowBox[{
RowBox[{"(",
RowBox[{"Length", "@", "listOfgrids"}], ")"}], "/", "2"}],
"]"}], "]"}], "*", "2"}], "]"}], "]"}]}], "]"}]}], ";"}]}],
"\n", "]"}]}], "]"}]}], ",",
RowBox[{"{",
RowBox[{"n", ",", "1", ",", "repeat"}], "}"}]}], "]"}]}]], "Code",
CellChangeTimes->{{3.8469460314718*^9, 3.846946081083346*^9},
3.846953193287258*^9},
CellLabel->"In[12]:=",
CellID->1203844417],
Cell[TextData[StyleBox["We then ran 30,000 random games and put the training \
data in an association. The reason we put it in an association was so we \
could connect a certain current board to the next logical move, making it \
easier later to build an actively learning AI.", "Text"]], "Text",
CellChangeTimes->{{3.846946209555315*^9, 3.846946232575992*^9}, {
3.8469462729891033`*^9, 3.846946345497849*^9}},
FontWeight->"Plain",
CellID->1454519220],
Cell[BoxData[{
RowBox[{
RowBox[{"runSoManyEveryTimes", "[", "30000", "]"}],
";"}], "\[IndentingNewLine]",
RowBox[{
RowBox[{"trainingData1", "=",
RowBox[{"Merge", "[",
RowBox[{"AI1", ",", "List"}], "]"}]}], ";"}], "\[IndentingNewLine]",
RowBox[{
RowBox[{"trainingData2", "=",
RowBox[{"Merge", "[",
RowBox[{"AI2", ",", "List"}], "]"}]}], ";"}]}], "Input",
InitializationCell->True,
CellChangeTimes->{{3.846946367469078*^9, 3.846946397619265*^9}, {
3.8471435822835703`*^9, 3.847143597588394*^9}, {3.8486103231485696`*^9,
3.848610333271551*^9}, {3.848612448567519*^9, 3.8486124842965803`*^9}, {
3.8486125241603994`*^9, 3.8486125261943398`*^9}, {3.848612578205849*^9,
3.848612587486924*^9}, 3.8486129468068824`*^9},
CellLabel->"In[13]:=",
CellID->1391277730],
Cell["\<\
Here's some basic info about the training data, including the wins for player \
1, the wins for player 2, and the amount of draws.\
\>", "Text",
CellChangeTimes->{{3.846946412049077*^9, 3.846946451020856*^9},
3.847138855823009*^9},
CellID->1786400792],
Cell[CellGroupData[{
Cell[BoxData[
RowBox[{"{",
RowBox[{
RowBox[{"Length", "@", "AI1"}], ",", " ",
RowBox[{"Length", "@", "AI2"}], ",", " ",
RowBox[{"30000", "-", " ",
RowBox[{"Length", "@", "AI1"}], " ", "-", " ",
RowBox[{"Length", "@", "AI2"}]}]}], "}"}]], "Input",
CellChangeTimes->{{3.846946459395767*^9, 3.846946460070756*^9}},
CellLabel->"In[16]:=",
CellID->812086743],
Cell[BoxData[
RowBox[{"{",
RowBox[{"16574", ",", "10471", ",", "2955"}], "}"}]], "Output",
CellChangeTimes->{
3.8469464654483843`*^9, 3.8469527484927998`*^9, 3.847138869201721*^9, {
3.8522841836468077`*^9, 3.852284188769905*^9}, 3.8522850773412957`*^9},
CellLabel->"Out[16]="]
}, Open ]],
Cell["Here's an example of a winning game for player 1:", "Text",
CellChangeTimes->{{3.84694647733134*^9, 3.846946495781891*^9}},
CellID->1845140062],
Cell[CellGroupData[{
Cell[BoxData[
RowBox[{"AI1", "[",
RowBox[{"[", "14789", "]"}], "]"}]], "Input",
CellChangeTimes->{{3.84694649915879*^9, 3.846946516865757*^9}},
CellLabel->"In[35]:=",
CellID->1590284773],
Cell[BoxData[
RowBox[{"\[LeftAssociation]",
RowBox[{
RowBox[{
RowBox[{"{",
RowBox[{
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "0"}], "}"}]}], "}"}], "\[Rule]",
RowBox[{"{",
RowBox[{
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"1", ",", "0", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "0"}], "}"}]}], "}"}]}], ",",
RowBox[{
RowBox[{"{",
RowBox[{
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"1", ",", "2", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "0"}], "}"}]}], "}"}], "\[Rule]",
RowBox[{"{",
RowBox[{
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"1", ",", "2", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "1"}], "}"}]}], "}"}]}], ",",
RowBox[{
RowBox[{"{",
RowBox[{
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"1", ",", "2", ",", "2"}], "}"}], ",",
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "1"}], "}"}]}], "}"}], "\[Rule]",
RowBox[{"{",
RowBox[{
RowBox[{"{",
RowBox[{"0", ",", "1", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"1", ",", "2", ",", "2"}], "}"}], ",",
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "1"}], "}"}]}], "}"}]}], ",",
RowBox[{
RowBox[{"{",
RowBox[{
RowBox[{"{",
RowBox[{"2", ",", "1", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"1", ",", "2", ",", "2"}], "}"}], ",",
RowBox[{"{",
RowBox[{"0", ",", "0", ",", "1"}], "}"}]}], "}"}], "\[Rule]",
RowBox[{"{",
RowBox[{
RowBox[{"{",
RowBox[{"2", ",", "1", ",", "0"}], "}"}], ",",
RowBox[{"{",
RowBox[{"1", ",", "2", ",", "2"}], "}"}], ",",
RowBox[{"{",
RowBox[{"1", ",", "0", ",", "1"}], "}"}]}], "}"}]}], ",",
RowBox[{
RowBox[{"{",
RowBox[{
RowBox[{"{",
RowBox[{"2", ",", "1", ",", "2"}], "}"}], ",",
RowBox[{"{",
RowBox[{"1", ",", "2", ",", "2"}], "}"}], ",",
RowBox[{"{",
RowBox[{"1", ",", "0", ",", "1"}], "}"}]}], "}"}], "\[Rule]",
RowBox[{"{",
RowBox[{
RowBox[{"{",
RowBox[{"2", ",", "1", ",", "2"}], "}"}], ",",
RowBox[{"{",
RowBox[{"1", ",", "2", ",", "2"}], "}"}], ",",
RowBox[{"{",
RowBox[{"1", ",", "1", ",", "1"}], "}"}]}], "}"}]}]}],
"\[RightAssociation]"}]], "Output",
CellChangeTimes->{3.8469465175735826`*^9, 3.8469527543416*^9,
3.847138871684639*^9},
CellLabel->"Out[35]=",
CellID->1726988935]
}, Open ]],
Cell["\<\
where player 1 wins by taking the diagonal and selecting the center spot.\
\>", "Text",
CellChangeTimes->{{3.846946521868568*^9, 3.846946538285254*^9}, {
3.846952809205755*^9, 3.846952825693756*^9}},
CellID->274975898]
}, Open ]],
Cell[CellGroupData[{
Cell["Actively Learning", "Subsection",
CellChangeTimes->{{3.846946553675214*^9, 3.846946556378241*^9}},
CellID->1575367380],
Cell["\<\
So far, we have AI1, a list of winning boards for player 1, and AI2, a list \
of winning boards for player 2. The variable trainingData nicely puts this \
information in an association.\
\>", "Text",
CellChangeTimes->{{3.846946560158289*^9, 3.846946560568787*^9}, {
3.8469470710087357`*^9, 3.8469471539202127`*^9}, {3.8481470712201576`*^9,
3.8481470858147974`*^9}},
CellID->1273357800],
Cell["\<\
To use this information to play, the AIs check what they have in their \
trainingData. If the tic-tac-toe board exists, the player chooses one of the \
options in its dataset. If the tic-tac-toe board doesn't exist, the player \
just moves randomly.\
\>", "Text",
CellChangeTimes->{{3.8469471570521107`*^9, 3.846947272518756*^9}},
CellID->1875876687],
Cell["\<\
For a significant number of training games in a player's dataset (these are \
random games), the training data for player 1 looks something like this:\
\>", "Text",
CellChangeTimes->{{3.846947314104907*^9, 3.846947341513348*^9}, {
3.8481471150364923`*^9, 3.8481471279640856`*^9}},
CellID->679346143],
Cell[CellGroupData[{
Cell[BoxData[
RowBox[{
RowBox[{"Dataset", "[",
RowBox[{"Counts", "[",
RowBox[{"Normal", "@", "trainingData1"}], "]"}], "]"}], "//",
"ReverseSort"}]], "Input",
CellChangeTimes->{{3.846947344206958*^9, 3.846947365383654*^9}},
CellLabel->"In[36]:=",
CellID->513305077],
Cell[BoxData[
GraphicsBox[
TagBox[RasterBox[CompressedData["
1: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
Dr/+3+7r+/v7pZ14fhjhd6dOOv6QnnlSDkb3cXbrMfI7iQlmuThoTWX+oorp
RIiDVoiG5H1JbjWdm4rRNp2bRePQVG7mrIfk1nJJDbRgtqZ9enryGbepmtYf
66YOdKNNp9Opf1AK9wH8vznz4g87CcdJP5JwvJJPOvnHtKk4xGxdptLJlalg
FopD5NZlKosqshMhDiVOf3LQkltXkJsq0V5BbpaLQ8zWZ2nlZuZ6SG6lpsWK
CTXtw8ND9xoNNa3QdDzr/ns4HJIv//t95rykdZOaNnLrkTZe06osqshOFE9/
/kw9e3KvWdPays0KtZyJ3Gy8ptVdVNS0gKKYZw/u7u6oaYUmf0wL3wwX7P5r
/538h+5yzrzUtH1TwaxT06ZNpcqiiuxE9/TnNzH1KGxQs6a1lZsbrGmXXmTF
azw3O9S0gCJq2qG082a4Wu++k/nug8z3HVDTdoRgVqhpk6dSZVFFdqJ++vO/
JdytrV/TWsnNrdW0U8GsU9PeNjc71LSAImraK/4IM3Wgm2oKB7f+gcL/WPKd
nPB22vAuhmTySSfcXkjehBCimK3HbyW/EzmYpeOQM5UqiyqyEyEOcoi6t36P
Gr1bKwctuXUduZm5IN1acrNCHG6em53k9ZDcmh9eoFnUtEM+5adePB1tujoo
hbfTpr0PJRwt8482pe/TCiGa3Xqk/E5mg1k0DplTqbKoIjtRv6XjtyssD3nx
JLeuIDdVbqCtIDdLx6GF3JzqrY/7tMBS1LRDS1/fdD//0S7DzwAM18Wzr3PJ
R9rIToKEmnZR/2mvb6rsgkowg3JxUJlKlUUldxLonv7uz4QfqPnsgTOVm8Ln
L7Wfm/E9JF9smsjNRYN01LSAKmraoYTzprt8dLY3WjaEJuFaPnw+zJWrbc12
Eu4YzL4EPHVMm+2/v6FhHGK2rrILKsF0xeKgMpX9H9ulLqqYTpz25x7Mvghb
uaZ1dnJTiHbmmqyTmzGDjAlmuTjUyc2YHjLXQ04rNS1WjJp2KO28KTueP147
/4Mx8ztx08e0+P6JQ7DuOMhNyerXtLJ25kKeiPbXZJ0FSRwyW6lpsWLUtKN0
DwjhkjzzjxKqdBKM7sLS/omD0InMRBxmmwptMafV+lxMjd/EmqywIImDSis1
LVYs8m8udBYXo0oq17ThJarRtxgITaPCNXvmYUSlE3d5lkylf+IQrDIOclM+
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