From 1f95eee492275ae3da9d59d0ea2687e975c44aae Mon Sep 17 00:00:00 2001 From: mprzymus Date: Thu, 17 Oct 2019 19:57:10 +0200 Subject: [PATCH 01/24] Solution All exercise with usage --- lista2/src/App/Test1.scala | 39 ++++++++++++++++++++++++++++++++++++++ lista2/src/App/test.sc | 11 +++++++++++ 2 files changed, 50 insertions(+) create mode 100644 lista2/src/App/Test1.scala create mode 100644 lista2/src/App/test.sc diff --git a/lista2/src/App/Test1.scala b/lista2/src/App/Test1.scala new file mode 100644 index 0000000..2c6ebed --- /dev/null +++ b/lista2/src/App/Test1.scala @@ -0,0 +1,39 @@ +package App + +class Test1 { + + def reverse [A](list : List[A]) : List[A] = { + def reverseIn (list : List[A], toReturn : List[A]) : List[A] = { + if (list == Nil) toReturn + else reverseIn(list.tail, list.head::toReturn) + } + reverseIn(list,List()) + } + + def divide (list : List[Int]) : (List[Int], List[Int]) = { + def divideIn(list : List[Int],negative : List[Int], negativeOdd : List[Int]) : (List[Int], List[Int]) = { + if (list != Nil) + (list.head < 0, list.head % 2 != 0) match { + case (true,true) => divideIn(list.tail,list.head::negative,list.head::negativeOdd) + case (true,false) => divideIn(list.tail,list.head::negative,negativeOdd) + case (false, _) => divideIn(list.tail,negative,negativeOdd) + } + else (reverse(negative),reverse(negativeOdd)) + } + divideIn(list,List(),List()) + } + + def length [A](list : List[A]) : Int = { + def lengthIn[A](list : List[A], listLength : Int) : Int = { + if (list == Nil) listLength + else lengthIn(list.tail,listLength+1) + } + lengthIn(list,0) + } + + def merge[A] (list1 : List[A], list2 : List[A]) : List[A] = { + if (list1 != Nil && list2 != Nil) list1.head::list2.head::merge(list1.tail,list2.tail) + else if (list1 != Nil) list1 + else list2 + } +} diff --git a/lista2/src/App/test.sc b/lista2/src/App/test.sc new file mode 100644 index 0000000..097cdef --- /dev/null +++ b/lista2/src/App/test.sc @@ -0,0 +1,11 @@ +import App.Test1 +val x = new Test1() +x.length(List(1,2,3)) +x.length(List()) +x.length(List(1,2)) + +x.merge(List(5,4,3,2),List(1,2,3,4,5,6)) +x.merge(List(),List()) +x.divide(List(-3,-6,8,-9,13)) +x.divide(List(3,6,8,9,13)) +x.divide(List()) \ No newline at end of file From 24e0bd53670c2a071678181727fbe807c0e29279 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Thu, 17 Oct 2019 20:12:44 +0200 Subject: [PATCH 02/24] 3 improved 3 with tail recursion --- lista2/out/production/lista 2/App/Test1.class | Bin 0 -> 4793 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 lista2/out/production/lista 2/App/Test1.class diff --git a/lista2/out/production/lista 2/App/Test1.class b/lista2/out/production/lista 2/App/Test1.class new file mode 100644 index 0000000000000000000000000000000000000000..237771b569d0b338b067f4481750dc4505550e64 GIT binary patch literal 4793 zcmbtXOH&)!75;8N)M}}D2rSzIY%DBnNjx;dFDxE*3v4V~V1pP4j2*jyHnc|%(8ICk z2c%M2dR9rLD%5P)WHFNkOvP16R!&tam0f;7enz|)=iF`~3!|VUQziAieIMt1=bZ1H zd;70H{NaxPuHq{ORo~u@Et;irTmfTfe_^~dVp$`<8GE?)!dx#a5E;Dog5lD-ku@03 zr|nLxXg<%a zs5OI;xSQJ7!AMZ0R`Epifz?E1J)*KcT}T?q9%je_JEQybwP{7@j;KbTE|zCp8)t?@ z!Pl+EeW}M2nFn2(!5&>JWERTP7j=(Mo@euYx+Juv1~+3}iSmOb-_DfBE5;{;k>HH4 z|C~BsoR);1iEFApEehv5<+y(D$>_HA)qS5nEsOd!b%#BPZ!EjUmr2jX88xU!zerU4 zD$5A6aA`Kq^kDLoC`(WPLtS2R(-M+Q>b{Zd_sw6H1SPU0-5c)e@+qcTQe>fJ zr{CubOM=vy2)X-&fW)NuDP6en{K@%{d)OsRiVMD-nRb%zV=YFzDqWEUc}nk0s{Wf( zdYnZ>zqLI3R}J0XVPNe1xsV;b7fuA&t_%8{yX zQu$Dv!E9>6*h$j=O9yJ`#3>I1_{qldmQ^Ac=~_ZlCZ1@5t%Sj~VZF3AD5mk|m=9yR zSlP~+Bc$NFrhCR4#J3hXN6vUNS2UFIe{Gs1%pS*JCs3uP1~d;;GbEpwOSCzM^E_<5 zl%JB`WSYv$#oY`Q?uS(#&Q5 zV}i>(!71(Rc7#qf8>D06&zqar3{7(i941D?+F~b~hCvK*9}a6!;o-f{7~JK;f=S0_ zo|s_nRE#X8us?h}Wg@E@uHdQ%60V(G$ha~EH+VrXtSrN+aOAi+4L6bSU=)-N(w1p# zXqa#YCK0?R`A(AcZvf zcHGKaWwK2UudYUxG|b_l2Qzp?*4ATCut4N_8s}ID3WFHto?pc09%Qh@=SHe?8h+x8 zu8@pFEas+JWH=p8)%%<<@^MQ0FYuTHdP4UsN9(LnUf;S?EEb9ip8f|zY_x_4&#=aD zxj`_Fhu5)igJUM2dw6oBt7V2!1#lJ3QYA}{>!^>k)7S8VlXDbb+Q!?;bDVa>P-&{b z&xy9Ql{e=qxiz!6$d7xHlP=I5aLFiI{JlD;)~X(-nfDB~v!m zznnF;tIIu^LZ!HF-nF>E0L7XIe#t6XBvH@j3uR(ZAx|6IGW@=o%4Z2hD*wDd{IW+# z9fFiq5?yUUDMrD92O_rVZGu{Xk46W6kB$SJ{+3<@?9kIi3qcEu^gQh>!Wo>UH!eg& zfaXio7vM#iTA9L)b{gzLcUAgr(#fT3wsc#;g-;cnsZ*fU6lg~j^c+8{8RX0Tg3%uWlcpZ zqPBl%HwLIDR6P~3m8k+vx6?#BI`~C}rgxpoe`roBSx!!B19Omjo@J>@I1^THh4O&9q_`pq_Q1gAsf8Q0q=Up&IKgwx@XYu`5r$dM^wdK_YV2J&d411O>PgaJ&0WDZAYr+NNth)+Aiobpk)^q zKatux=x=po4B0}$ABFTeLfS-s+b%k)`v#q5%vtsp&sGA;0oMQY7MYdkAaN%;M2`bJ zuLX)-g3k_=QVSFh&c|T!AU2p2+~}s?2OhrTJ4I)feV~AxINfl!RNc*fz@mZ~#{LCC z_<%{Y9@9kAgn)k?lMw>kjTg}73utdV$G7t`bJF(rRBbq2&y!m~-xb|t)?M=3Z^$?Q zL??5jopr!(Q#i_t919&%n0ii~;$zb`g?aA|^+Y=pTSS}8(FVU*WwVfIIq=5DD?14&zr!L=K&laxfGJ+a61CjK&5yY`e8^#^wh&uyB@~`a z3HMOIO^L+`KV_%EH?{)RQJ}s<7nh>i3fcw|o}qweAK5)6FyZC={ZtQF9hmHA;ACuaxmKYP~ea w&se2I_4gGG&^4W?&UM&FDE}6s4^=wrI4c?W$;7Y9&hgF94TjgWO_T-v8}#oJIRF3v literal 0 HcmV?d00001 From 51d92a2486d29324147610515ca7ad592f66b9d2 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Thu, 24 Oct 2019 19:17:41 +0200 Subject: [PATCH 03/24] 1,2 1 for one pattern --- lista3/src/App/App.scala | 61 ++++++++++++++++++++++++++++++++++++++++ lista3/src/App/test.sc | 15 ++++++++++ 2 files changed, 76 insertions(+) create mode 100644 lista3/src/App/App.scala create mode 100644 lista3/src/App/test.sc diff --git a/lista3/src/App/App.scala b/lista3/src/App/App.scala new file mode 100644 index 0000000..39bd97c --- /dev/null +++ b/lista3/src/App/App.scala @@ -0,0 +1,61 @@ +package App + +class App { + + def findPatterns(list : List[String], pattern : List[String]) : List[String] = { + list.filter( element => element.contains(pattern)) + } + + def contains(pattern : String, string : String) : Boolean = { + if (pattern.isEmpty()) true + else if (string.isEmpty()) false + else if (string.head == pattern.head) contains(pattern.tail, string.tail) + else false + } + + //zad1 + def find(list : List[String], pattern : String) : List[String] = { + list.filter( element => contains(pattern,element)) + } + + def find2(list : List[String], pattern : String) : List[String] = { + if (list == Nil) Nil + else if (contains(pattern,list.head)) list.head::find2(list.tail,pattern) + else find2(list.tail,pattern) + } + + def find2Tail(list : List[String], pattern : String) : List[String] = { + def find2Helper(list : List[String], pattern : String, result : List[String]) : List[String] = { + if (list == Nil) result + else if (contains(pattern,list.head)) find2Helper(list.tail,pattern, list.head::result) + else find2Helper(list.tail,pattern,result) + } + reverse(find2Helper(list, pattern, List())) + } + + //zad2 + def reverse [A](list : List[A]) : List[A] = { + def reverseHelper (list : List[A], toReturn : List[A]) : List[A] = { + if (list == Nil) toReturn + else reverseHelper(list.tail, list.head::toReturn) + } + reverseHelper(list,List()) + } + + def joinList[A](list1 : List[A], list2 : List[A], list3 : List[A]) : List[A] = { + def merge2[A](list1: List[A], list2: List[A]) : List[A] = { + if (list1 == Nil) list2 + else list1.head::merge2(list1.tail, list2) + } + merge2(list1,merge2(list2,list3)) + } + + def joinListTail[A](list1 : List[A], list2 : List[A], list3 : List[A]) : List[A] = { + def joinListHelper[A](list : List[A], result : List[A], listOfLists : List[List[A]]) : List[A] = { + if (list != Nil) joinListHelper(list.tail, list.head::result, listOfLists) + else if (listOfLists == Nil) result + else joinListHelper(listOfLists.head,result, listOfLists.tail) + } + reverse(joinListHelper(list1,List(),List(list2, list3))) + } +} diff --git a/lista3/src/App/test.sc b/lista3/src/App/test.sc new file mode 100644 index 0000000..6fddf21 --- /dev/null +++ b/lista3/src/App/test.sc @@ -0,0 +1,15 @@ +import App.App + +val x = new App() + +x.joinList(List(1,2,3),List(4,5,6), List(7,8,9)) +x.joinListTail(List(1,2,3),List(4,5,6), List(7,8,9)) + +x.find(List("inde","index0168202","indeasdsd","in", + "index0169222","index0dasd"), "index") + +x.find2(List("inde","index0168202","indeasdsd","in", + "index0169222","index0dasd"), "index") + +x.find2Tail(List("inde","index0168202","indeasdsd","in", + "index0169222","index0dasd"), "index") \ No newline at end of file From 2102771681eaf9b5dd1d37a6e71281fb7d6b9397 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Thu, 24 Oct 2019 19:32:37 +0200 Subject: [PATCH 04/24] 1 finished --- lista3/src/App/App.scala | 22 +++++++++++++++++----- lista3/src/App/test.sc | 13 ++++++++++--- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/lista3/src/App/App.scala b/lista3/src/App/App.scala index 39bd97c..99fc91d 100644 --- a/lista3/src/App/App.scala +++ b/lista3/src/App/App.scala @@ -13,18 +13,30 @@ class App { else false } + def contains(patterns : List[String], string : String) : Boolean = { + if (patterns == Nil) false + else if (contains(patterns.head, string)) true + else contains(patterns.tail, string) + } + //zad1 - def find(list : List[String], pattern : String) : List[String] = { - list.filter( element => contains(pattern,element)) + def findListOfPatterns(list : List[String], pattern : List[String]) : List[String] = { + if (list == Nil) Nil + else if (contains(pattern,list.head)) list.head::findListOfPatterns(list.tail,pattern) + else findListOfPatterns(list.tail,pattern) } def find2(list : List[String], pattern : String) : List[String] = { + list.filter( element => contains(pattern,element)) + } + + def find(list : List[String], pattern : String) : List[String] = { if (list == Nil) Nil - else if (contains(pattern,list.head)) list.head::find2(list.tail,pattern) - else find2(list.tail,pattern) + else if (contains(pattern,list.head)) list.head::find(list.tail,pattern) + else find(list.tail,pattern) } - def find2Tail(list : List[String], pattern : String) : List[String] = { + def findTail(list : List[String], pattern : String) : List[String] = { def find2Helper(list : List[String], pattern : String, result : List[String]) : List[String] = { if (list == Nil) result else if (contains(pattern,list.head)) find2Helper(list.tail,pattern, list.head::result) diff --git a/lista3/src/App/test.sc b/lista3/src/App/test.sc index 6fddf21..4632777 100644 --- a/lista3/src/App/test.sc +++ b/lista3/src/App/test.sc @@ -5,11 +5,18 @@ val x = new App() x.joinList(List(1,2,3),List(4,5,6), List(7,8,9)) x.joinListTail(List(1,2,3),List(4,5,6), List(7,8,9)) +x.find2(List("inde","index0168202","indeasdsd","in", + "index0169222","index0dasd"), "index") + x.find(List("inde","index0168202","indeasdsd","in", "index0169222","index0dasd"), "index") -x.find2(List("inde","index0168202","indeasdsd","in", +x.findTail(List("inde","index0168202","indeasdsd","in", "index0169222","index0dasd"), "index") -x.find2Tail(List("inde","index0168202","indeasdsd","in", - "index0169222","index0dasd"), "index") \ No newline at end of file +x.findListOfPatterns(List("a","aaa","bbb","aaaa", "bbbb", "cc","cccc", + "cccccccc"), List("aa","bb")) + +x.find(List("aaa","bbb","aaaa", "bbbb", "cc","cccc", + "cccccccc"), "aa") + From 80663d0021094eb902ed8b27c98c389840ebc807 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Thu, 24 Oct 2019 19:48:08 +0200 Subject: [PATCH 05/24] 1 tail recursive --- lista3/src/App/App.scala | 9 +++++++++ lista3/src/App/test.sc | 10 +++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lista3/src/App/App.scala b/lista3/src/App/App.scala index 99fc91d..f1cf14a 100644 --- a/lista3/src/App/App.scala +++ b/lista3/src/App/App.scala @@ -26,6 +26,15 @@ class App { else findListOfPatterns(list.tail,pattern) } + def findListOfPatternsTail(list : List[String], pattern : List[String]) : List[String] = { + def helper(list : List[String], pattern : List[String], result : List[String]) : List[String] = { + if (list == Nil) result + else if (contains(pattern,list.head)) helper(list.tail,pattern,list.head::result) + else helper(list.tail,pattern,result) + } + reverse(helper(list,pattern,List())) + } + def find2(list : List[String], pattern : String) : List[String] = { list.filter( element => contains(pattern,element)) } diff --git a/lista3/src/App/test.sc b/lista3/src/App/test.sc index 4632777..0d7fb79 100644 --- a/lista3/src/App/test.sc +++ b/lista3/src/App/test.sc @@ -5,18 +5,18 @@ val x = new App() x.joinList(List(1,2,3),List(4,5,6), List(7,8,9)) x.joinListTail(List(1,2,3),List(4,5,6), List(7,8,9)) -x.find2(List("inde","index0168202","indeasdsd","in", - "index0169222","index0dasd"), "index") - x.find(List("inde","index0168202","indeasdsd","in", "index0169222","index0dasd"), "index") +x.find(List("aaa","bbb","aaaa", "bbbb", "cc","cccc", + "cccccccc"), "aa") + x.findTail(List("inde","index0168202","indeasdsd","in", "index0169222","index0dasd"), "index") x.findListOfPatterns(List("a","aaa","bbb","aaaa", "bbbb", "cc","cccc", "cccccccc"), List("aa","bb")) -x.find(List("aaa","bbb","aaaa", "bbbb", "cc","cccc", - "cccccccc"), "aa") +x.findListOfPatternsTail(List("a","aaa","bbb","aaaa", "bbbb", "cc","cccc", + "cccccccc"), List("aa","bb")) From bfacd3107b1cdf3fff8ad7df974664f44f85ac8d Mon Sep 17 00:00:00 2001 From: mprzymus Date: Thu, 7 Nov 2019 12:49:53 +0100 Subject: [PATCH 06/24] Rename --- lista2/src/App/{Test1.scala => Lista2.scala} | 15 ++++++++++++--- lista2/src/App/{test.sc => lista2.sc} | 1 + lista3/src/App/{App.scala => Lista3.scala} | 0 lista3/src/App/{test.sc => lista3.sc} | 14 ++++++++++++-- 4 files changed, 25 insertions(+), 5 deletions(-) rename lista2/src/App/{Test1.scala => Lista2.scala} (68%) rename lista2/src/App/{test.sc => lista2.sc} (86%) rename lista3/src/App/{App.scala => Lista3.scala} (100%) rename lista3/src/App/{test.sc => lista3.sc} (70%) diff --git a/lista2/src/App/Test1.scala b/lista2/src/App/Lista2.scala similarity index 68% rename from lista2/src/App/Test1.scala rename to lista2/src/App/Lista2.scala index 2c6ebed..0be78e1 100644 --- a/lista2/src/App/Test1.scala +++ b/lista2/src/App/Lista2.scala @@ -3,11 +3,11 @@ package App class Test1 { def reverse [A](list : List[A]) : List[A] = { - def reverseIn (list : List[A], toReturn : List[A]) : List[A] = { + def reverseHelper (list : List[A], toReturn : List[A]) : List[A] = { if (list == Nil) toReturn - else reverseIn(list.tail, list.head::toReturn) + else reverseHelper(list.tail, list.head::toReturn) } - reverseIn(list,List()) + reverseHelper(list,List()) } def divide (list : List[Int]) : (List[Int], List[Int]) = { @@ -32,8 +32,17 @@ class Test1 { } def merge[A] (list1 : List[A], list2 : List[A]) : List[A] = { + def mergeIn[A](list1 : List[A], list2 : List[A], result : List[A]) : List[A] = {//mergeHelper nazwać!!! + if (list1 != Nil && list2 != Nil) mergeIn(list1.tail,list2.tail, list2.head::list1.head::result) + else if (list1 != Nil) list1:::result + else list2:::result + } + reverse(mergeIn(list1,list2, List())) + /* if (list1 != Nil && list2 != Nil) list1.head::list2.head::merge(list1.tail,list2.tail) else if (list1 != Nil) list1 else list2 + rekursja zwykla + */ } } diff --git a/lista2/src/App/test.sc b/lista2/src/App/lista2.sc similarity index 86% rename from lista2/src/App/test.sc rename to lista2/src/App/lista2.sc index 097cdef..f1833fa 100644 --- a/lista2/src/App/test.sc +++ b/lista2/src/App/lista2.sc @@ -5,6 +5,7 @@ x.length(List()) x.length(List(1,2)) x.merge(List(5,4,3,2),List(1,2,3,4,5,6)) +x.merge(List(1,3,5,7),List(2,4,6)) x.merge(List(),List()) x.divide(List(-3,-6,8,-9,13)) x.divide(List(3,6,8,9,13)) diff --git a/lista3/src/App/App.scala b/lista3/src/App/Lista3.scala similarity index 100% rename from lista3/src/App/App.scala rename to lista3/src/App/Lista3.scala diff --git a/lista3/src/App/test.sc b/lista3/src/App/lista3.sc similarity index 70% rename from lista3/src/App/test.sc rename to lista3/src/App/lista3.sc index 0d7fb79..3db77b9 100644 --- a/lista3/src/App/test.sc +++ b/lista3/src/App/lista3.sc @@ -1,6 +1,6 @@ -import App.App +import App.Lista3 -val x = new App() +val x = new Lista3() x.joinList(List(1,2,3),List(4,5,6), List(7,8,9)) x.joinListTail(List(1,2,3),List(4,5,6), List(7,8,9)) @@ -20,3 +20,13 @@ x.findListOfPatterns(List("a","aaa","bbb","aaaa", "bbbb", "cc","cccc", x.findListOfPatternsTail(List("a","aaa","bbb","aaaa", "bbbb", "cc","cccc", "cccccccc"), List("aa","bb")) +sealed trait BT[+A] +case object Empty extends BT[Nothing] +case class Node[+A](elem:A, left:BT[A], right:BT[A]) extends BT[A] + +val t = Node(1,Node(2,Empty,Node(3,Empty,Empty)),Empty) + +val t1 = t.left + + + From b562ec95797b2f4ba204c2620d6ac4f2828c4390 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Thu, 7 Nov 2019 18:57:35 +0100 Subject: [PATCH 07/24] list4 --- lista4/src/App/Lista4.scala | 5 +++++ lista4/src/App/lista4worksheet.sc | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 lista4/src/App/Lista4.scala create mode 100644 lista4/src/App/lista4worksheet.sc diff --git a/lista4/src/App/Lista4.scala b/lista4/src/App/Lista4.scala new file mode 100644 index 0000000..e309c9d --- /dev/null +++ b/lista4/src/App/Lista4.scala @@ -0,0 +1,5 @@ +package App + +class Lista4 { + +} diff --git a/lista4/src/App/lista4worksheet.sc b/lista4/src/App/lista4worksheet.sc new file mode 100644 index 0000000..718c01e --- /dev/null +++ b/lista4/src/App/lista4worksheet.sc @@ -0,0 +1,17 @@ +import App.Lista4 + +val x = new Lista4 + +x.filter(List(List(4,4,2,1,4), List(1,2,23), List(23,324,4)), 23) + +x.decToHex(31) +x.decToHex(32) +x.decToHex(16385) + +x.decToAny(31,16) +x.decToAny(32,16) +x.decToAny(16385,16) + +x.decToAny(1119,16) +x.decToAny(1119,8) +x.decToAny(1119,2) \ No newline at end of file From 406847b6d05d6b9164ca9ac6bb8bb72b9a85d5d5 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Thu, 7 Nov 2019 18:59:25 +0100 Subject: [PATCH 08/24] Lista4 --- lista4/src/App/Lista4.scala | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/lista4/src/App/Lista4.scala b/lista4/src/App/Lista4.scala index e309c9d..7b17d07 100644 --- a/lista4/src/App/Lista4.scala +++ b/lista4/src/App/Lista4.scala @@ -1,5 +1,30 @@ package App class Lista4 { - + def filter[A](list : List[List[A]],op: A) : List[List[A]] = { + def filterHelper(list : List[List[A]],element: A, result : List[List[A]]) : List[List[A]] = { + if (list == Nil) result + else if (list.head.contains(element)) filterHelper(list.tail,element, list.head::result) + else filterHelper(list.tail,element,result) + } + filterHelper(list,op,List()).reverse + } + def decToHex(number : Int) : List[Int] = { + def decToHexHelper(number : Int, acc : List[Int]) : List[Int] = { + val result = number / 16 + val rest = number % 16 + if (result == 0) rest::acc + else decToHexHelper(result,rest::acc) + } + decToHexHelper(number, List()) + } + def decToAny(number : Int, convertTo : Int) : List[Int] = { + def decToHexHelper(number : Int, acc : List[Int]) : List[Int] = { + val result = number / convertTo + val rest = number % convertTo + if (result == 0) rest::acc + else decToHexHelper(result,rest::acc) + } + decToHexHelper(number, List()) + } } From 72e106c30df847f7bb13378350ece3d3e3346d57 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Thu, 7 Nov 2019 19:27:16 +0100 Subject: [PATCH 09/24] List4 fix --- lista4/src/App/Lista4.scala | 10 ++++++---- lista4/src/App/lista4worksheet.sc | 11 +++++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lista4/src/App/Lista4.scala b/lista4/src/App/Lista4.scala index 7b17d07..6591380 100644 --- a/lista4/src/App/Lista4.scala +++ b/lista4/src/App/Lista4.scala @@ -16,15 +16,17 @@ class Lista4 { if (result == 0) rest::acc else decToHexHelper(result,rest::acc) } - decToHexHelper(number, List()) + if (number >= 0) 1::decToHexHelper(number, List()) + else -1::decToHexHelper(-number, List()) } def decToAny(number : Int, convertTo : Int) : List[Int] = { - def decToHexHelper(number : Int, acc : List[Int]) : List[Int] = { + def decToAnyHelper(number : Int, acc : List[Int]) : List[Int] = { val result = number / convertTo val rest = number % convertTo if (result == 0) rest::acc - else decToHexHelper(result,rest::acc) + else decToAnyHelper(result,rest::acc) } - decToHexHelper(number, List()) + if (number >= 0) 1::decToAnyHelper(number, List()) + else -1::decToAnyHelper(-number, List()) } } diff --git a/lista4/src/App/lista4worksheet.sc b/lista4/src/App/lista4worksheet.sc index 718c01e..9a07c94 100644 --- a/lista4/src/App/lista4worksheet.sc +++ b/lista4/src/App/lista4worksheet.sc @@ -8,10 +8,13 @@ x.decToHex(31) x.decToHex(32) x.decToHex(16385) -x.decToAny(31,16) -x.decToAny(32,16) -x.decToAny(16385,16) +x.decToAny(-31,16) +x.decToAny(-32,16) +x.decToAny(-16385,16) x.decToAny(1119,16) +x.decToAny(-1119,16) x.decToAny(1119,8) -x.decToAny(1119,2) \ No newline at end of file +x.decToAny(-1119,8) +x.decToAny(1119,2) +x.decToAny(-1119,2) \ No newline at end of file From 91f6c19186472c6165e4af49aedf597d2c72f7bb Mon Sep 17 00:00:00 2001 From: mprzymus Date: Thu, 14 Nov 2019 17:26:52 +0100 Subject: [PATCH 10/24] 1,2, 3 in pregress --- lista5/src/lista5/Lista5.scala | 5 +++++ lista5/src/lista5/lista5ws.sc | 0 2 files changed, 5 insertions(+) create mode 100644 lista5/src/lista5/Lista5.scala create mode 100644 lista5/src/lista5/lista5ws.sc diff --git a/lista5/src/lista5/Lista5.scala b/lista5/src/lista5/Lista5.scala new file mode 100644 index 0000000..6a5d5c7 --- /dev/null +++ b/lista5/src/lista5/Lista5.scala @@ -0,0 +1,5 @@ +package lista5 + +class lista5 { + +} diff --git a/lista5/src/lista5/lista5ws.sc b/lista5/src/lista5/lista5ws.sc new file mode 100644 index 0000000..e69de29 From 455a86c2a42f49b93336ddc76d188e27efbababe Mon Sep 17 00:00:00 2001 From: mprzymus Date: Fri, 15 Nov 2019 00:09:11 +0100 Subject: [PATCH 11/24] 3a --- lista5/src/lista5/Lista5.scala | 74 +++++++++++++++++++++++++++++++++- lista5/src/lista5/lista5ws.sc | 12 ++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/lista5/src/lista5/Lista5.scala b/lista5/src/lista5/Lista5.scala index 6a5d5c7..399cf41 100644 --- a/lista5/src/lista5/Lista5.scala +++ b/lista5/src/lista5/Lista5.scala @@ -1,5 +1,75 @@ package lista5 -class lista5 { +import scala.util.Random -} +sealed trait BTree [A] +case class Empty[A]()extends BTree[A] +case class Node[A](elem:A, children: (BTree[A], BTree[A])) extends BTree[A] + + +class Lista5 { + + private val r = new Random(); + + def generateTree(depth : Int, maxValue : Int) : BTree[Int] = { + if (0 >= maxValue) throw new IllegalArgumentException("minValue >= maxValue") + if (depth >= 0) Node[Int](r.nextInt(maxValue) + 1,(generateTree(depth-1,maxValue),generateTree(depth-1, maxValue))) + else Empty[Int] + } + + def multiplyElements(tree : BTree[Int]) : Int = { + def multiplyHelper(tree : BTree[Int]) : Int = { + tree match { + case Node(element: Int, (left: BTree[Int], right: BTree[Int])) => element * multiplyHelper(left) * multiplyHelper(right) + case Empty() => 1 + } + } + tree match { + case node : Node[Int] => multiplyHelper(tree) + case _ => throw new IllegalArgumentException() + } + } + + def deleteDuplicatesBreadth(tree : BTree[Int]) : BTree[Int] = { + def generateList(queue : List[BTree[Int]], elements : List[Int]) : List[Int]= { + queue match { + case Nil => Nil + case Empty()::tail => generateList(tail,elements) + case Node(value,(left,right))::tail => + if (!elements.contains(value)) value::generateList(tail:::List(left,right), value::elements) + else -1::generateList(tail:::List(left,right),elements) + } + } + def listToTree(list: List[Int]): BTree[Int] = { + Empty() + } + val list = generateList(List(tree),List()) + println(list) + listToTree(list) + } + + def deleteDuplicatesDepth(tree: BTree[Int]): BTree[Int] = { + def generateList(stack : List[BTree[Int]], elements : List[Int]) : List[Int]= { + stack match { + case Nil => Nil + case Empty()::tail => generateList(tail,elements) + case Node(value,(left,right))::tail => + if (!elements.contains(value)) value::generateList(left::right::tail, value::elements) + else -1::generateList(left::right::tail,elements) + } + } + + def listToTree(list: List[Int]): BTree[Int] = { + list match { + case Nil => Empty() + case head :: tail => + val pairOfLists = tail.splitAt(tail.length/2) + Node(head, (listToTree(pairOfLists._1), listToTree(pairOfLists._2))) + } + } + val list = generateList(List(tree),List()) + println(list) + listToTree(list) + + } +} \ No newline at end of file diff --git a/lista5/src/lista5/lista5ws.sc b/lista5/src/lista5/lista5ws.sc index e69de29..601c976 100644 --- a/lista5/src/lista5/lista5ws.sc +++ b/lista5/src/lista5/lista5ws.sc @@ -0,0 +1,12 @@ +import lista5.Lista5 + +val x = new Lista5(); + +val tree = x.generateTree(1,10) +x.multiplyElements(tree) +x.deleteDuplicatesDepth(tree) +x.deleteDuplicatesBreadth(tree) + +val tree2 = x.generateTree(2,4) +x.deleteDuplicatesDepth(tree2) +x.deleteDuplicatesBreadth(tree2) From 6a4d6f9eff771752d10639755324b5acfd8ab29d Mon Sep 17 00:00:00 2001 From: mprzymus Date: Thu, 16 Jan 2020 14:57:46 +0100 Subject: [PATCH 12/24] player vs player kalaha --- lista10-kalahaGame/build.sbt | 8 ++ .../src/main/scala/Actors/Player.scala | 33 +++++ .../src/main/scala/Actors/Server.scala | 51 +++++++ .../scala/GameObjects/Ai/HumanPlayer.scala | 14 ++ .../scala/GameObjects/Ai/MoveDecider.scala | 6 + .../src/main/scala/GameObjects/Ai/Tree.scala | 6 + .../GameObjects/Output/ConsoleOutput.scala | 19 +++ .../scala/GameObjects/Output/Output.scala | 7 + .../scala/GameObjects/Utilities/Board.scala | 134 ++++++++++++++++++ .../Utilities/InvalidSenderException.scala | 5 + .../scala/GameObjects/Utilities/Main.scala | 9 ++ .../Utilities/PlayerPosition.scala | 14 ++ .../scala/GameObjects/Utilities/Timer.scala | 12 ++ .../Output/ConsoleOutputTest.scala | 12 ++ .../GameObjects/Utilities/BoardTest.scala | 126 ++++++++++++++++ 15 files changed, 456 insertions(+) create mode 100644 lista10-kalahaGame/build.sbt create mode 100644 lista10-kalahaGame/src/main/scala/Actors/Player.scala create mode 100644 lista10-kalahaGame/src/main/scala/Actors/Server.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/Ai/HumanPlayer.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/Ai/MoveDecider.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/Ai/Tree.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/Output/ConsoleOutput.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/Output/Output.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/Utilities/InvalidSenderException.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Main.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/Utilities/PlayerPosition.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Timer.scala create mode 100644 lista10-kalahaGame/src/test/scala/GameObjects/Output/ConsoleOutputTest.scala create mode 100644 lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala diff --git a/lista10-kalahaGame/build.sbt b/lista10-kalahaGame/build.sbt new file mode 100644 index 0000000..3b378f0 --- /dev/null +++ b/lista10-kalahaGame/build.sbt @@ -0,0 +1,8 @@ +name := "lista10-kalahaGame" + +version := "0.1" + +scalaVersion := "2.13.1" + +libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.6.1" +libraryDependencies += "org.scalatest" % "scalatest_2.13" % "3.1.0" % "test" diff --git a/lista10-kalahaGame/src/main/scala/Actors/Player.scala b/lista10-kalahaGame/src/main/scala/Actors/Player.scala new file mode 100644 index 0000000..15c49f4 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/Actors/Player.scala @@ -0,0 +1,33 @@ +package Actors + +import Actors.Player.{MakeMove, Move} +import Actors.Server.BadMove +import GameObjects.AI.MoveDecider +import akka.actor.Actor + +class Player(val playerDecider: MoveDecider) extends Actor { + override def receive: Receive = { + case MakeMove() => makeMove() + case BadMove() => makeMove() + + } + + private def makeMove(): Unit = { + try { + val move = Move(playerDecider.getMove) + context.parent ! move + } + catch { + case e: IllegalAccessException => + playerDecider.badMoveInform(e.getMessage) + throw e + } + } +} + +object Player { + + case class MakeMove() + + case class Move(house: Int) //to consider making houses enum +} diff --git a/lista10-kalahaGame/src/main/scala/Actors/Server.scala b/lista10-kalahaGame/src/main/scala/Actors/Server.scala new file mode 100644 index 0000000..f239f26 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/Actors/Server.scala @@ -0,0 +1,51 @@ +package Actors + +import Actors.Player.{MakeMove, Move} +import Actors.Server.BadMove +import GameObjects.AI.HumanPlayer +import GameObjects.Utilities.{Board, GameFinished, InvalidSenderException, PlayerLower, PlayerPosition, PlayerUpper, Timer} +import akka.actor.{Actor, ActorRef, Props} + +class Server(private val timeForTurn : Long = 10) extends Actor{ + val timer = new Timer() + val board = new Board(6) + val upperChild: ActorRef = context.actorOf(Props(classOf[Player], new HumanPlayer(board))) + val lowerChild: ActorRef = context.actorOf(Props(classOf[Player], new HumanPlayer(board))) + upperChild ! MakeMove() + override def receive: Receive = { + case turn : Move => + try { + if (timer.getTimeSeconds < timeForTurn) { + if (sender() == upperChild) { + val destination = board.move(turn.house, PlayerUpper()) + findDestination(destination) ! MakeMove() + } else if (sender() == lowerChild) { + val destination = board.move(turn.house, PlayerLower()) + findDestination(destination) ! MakeMove() + } else throw new InvalidSenderException("Unknown sender") + timer.restart() + } + else println("time out") + } + catch { + case e : IllegalArgumentException => { + println("here") + sender() ! BadMove() + } + } + case GameFinished() => { + println("Game Finished!") + } + } + private def findDestination(sendTo : PlayerPosition) ={ + sendTo match { + case PlayerUpper() => upperChild + case PlayerLower() => lowerChild + case _ => throw new IllegalArgumentException + } + } +} + +object Server { + case class BadMove() +} \ No newline at end of file diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Ai/HumanPlayer.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Ai/HumanPlayer.scala new file mode 100644 index 0000000..be52b3b --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Ai/HumanPlayer.scala @@ -0,0 +1,14 @@ +package GameObjects.AI + +import GameObjects.Outputs.ConsoleOutput +import GameObjects.Utilities.Board + +class HumanPlayer(private val board: Board) extends MoveDecider { + private val view = new ConsoleOutput(board) + override def getMove: Int = { + view.printGame() + scala.io.StdIn.readInt() + } + + override def badMoveInform(message: String): Unit = view.putMessage(message) +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Ai/MoveDecider.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Ai/MoveDecider.scala new file mode 100644 index 0000000..8a17d8e --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Ai/MoveDecider.scala @@ -0,0 +1,6 @@ +package GameObjects.AI + +trait MoveDecider { + def getMove : Int + def badMoveInform(message : String) : Unit +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Ai/Tree.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Ai/Tree.scala new file mode 100644 index 0000000..5aab48b --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Ai/Tree.scala @@ -0,0 +1,6 @@ +package GameObjects.AI + +trait Tree { + case class Node(houseToMove : Int) + case class Empty() +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Output/ConsoleOutput.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Output/ConsoleOutput.scala new file mode 100644 index 0000000..bdfba68 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Output/ConsoleOutput.scala @@ -0,0 +1,19 @@ +package GameObjects.Outputs + +import GameObjects.Utilities.Board + +class ConsoleOutput(private val board: Board) extends Output { + override def printGame(): Unit = { + println("A") + print("end zone: (" + board.playerUpperScore + ") ") + val uppers = board.playerUpperPits + uppers.foldRight()((x, Unit) => (print(x + " "))) //TODO must be better method + println() + for (i <- 0 to 5) + print(board.playerLowerPitHouseValue(i) + " ") + println(s"end zone: (${board.playerLowerScore})") + println("B") + } + + override def putMessage(message: String): Unit = println("message") +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Output/Output.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Output/Output.scala new file mode 100644 index 0000000..e52aae1 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Output/Output.scala @@ -0,0 +1,7 @@ +package GameObjects.Outputs + +trait Output { + def printGame(): Unit + + def putMessage(message: String): Unit +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala new file mode 100644 index 0000000..58c28e2 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala @@ -0,0 +1,134 @@ +package GameObjects.Utilities + +class Board(seedsInPit: Int) { + private val pits: Array[Int] = Array.fill(14) { + seedsInPit + } + private val storeA = 6 + private val storeB = 13 + pits(storeA) = 0 + pits(storeB) = 0 + + def playerUpperPitHouseValue(index: Int): Int = { + require(index >= 0 && index < storeA) + pits(index + 7) + } + + def playerUpperPits: Array[Int] = pits.slice(storeA + 1, storeB) + + def playersStoreIndex(playerPosition: PlayerPosition): Int = { + playerPosition match { + case PlayerUpper() => storeB + case PlayerLower() => storeA + case _ => throw new IllegalArgumentException("invalid playerStore call") + } + } + + def playerLowerPitHouseValue(index: Int): Int = { + require(index >= 0 && index < storeA) + pits(index) + } + + def playerLowerPits: Array[Int] = pits.slice(0, storeA) + + def playerPits(playerPosition: PlayerPosition): Array[Int] = { + playerPosition match { + case PlayerUpper() => playerUpperPits + case PlayerLower() => playerLowerPits + case _ => throw new IllegalArgumentException("invalid playerPits call") + } + } + + def playerUpperScore: Int = pits(storeB) + + def playerLowerScore: Int = pits(storeA) + + def playerScore(player: PlayerPosition) = { + player match { + case PlayerUpper() => playerUpperScore + case PlayerLower() => playerLowerScore + case _ => throw new IllegalArgumentException("invalid playerScore call") + } + } + + def move(pit: Int, playerPosition: PlayerPosition): PlayerPosition = { + require(pit >= 0, "Pit cannot be negative") + require(pit < storeA, s"Pit cannot be bigger than 5") + val realIndex = findRealIndex(pit, playerPosition) + val opponentsStore = opponentsStoreIndex(realIndex) + val seeds = pits(realIndex) + if (pits(realIndex) == 0) throw new IllegalArgumentException("Cannot move from empty pit") + pits(realIndex) = 0 + + @scala.annotation.tailrec + def pitVisitor(previousIndex: Int, seeds: Int): PlayerPosition = { + if (seeds > 0) { + val index = (previousIndex + 1 + (if (previousIndex + 1 == opponentsStore) 1 else 0)) % pits.length + pits(index) += 1 + pitVisitor(index, seeds - 1) + } + else if (!isHouse(previousIndex)) { + if (sumPoints(playerPosition) == 0) finishGame(playerPosition) + else playerPosition + } + else if (samePlayers(pit, previousIndex) && pits(previousIndex) == 1) { + val opposite = oppositeIndex(previousIndex) + val store = playersStoreIndex(previousIndex) + pits(store) += pits(opposite) + pits(opposite) = 0 + pits(previousIndex) = 0 + pits(store) += 1 + if (sumPoints(playerPosition) == 0) finishGame(playerPosition) + else playerPosition.opponent + } + else playerPosition.opponent + } + + pitVisitor(realIndex, seeds) + } + + private def findRealIndex(index: Int, player: PlayerPosition) = { + player match { + case PlayerLower() => index + case PlayerUpper() => index + 7 + } + } + + private def isHouse(index: Int) = index != storeA && index != storeB + + private def samePlayers(a: Int, b: Int) = (a < storeA) == (b < storeA) + + private def oppositeIndex(index: Int): Int = { + val lastPit = storeB - 1 + lastPit - index + } + + private def playersStoreIndex(index: Int) = { + if (index <= storeA) storeA + else storeB + } + + private def opponentsStoreIndex(index: Int) = { + if (index <= storeA) storeB + else storeA + } + + private def sumPoints(player: PlayerPosition) = playerPits(player).sum + + private def finishGame(winner: PlayerPosition): PlayerPosition = { + pits(playersStoreIndex(winner)) += sumPoints(winner.opponent) + val losersStore = playersStoreIndex(winner.opponent) + for (i <- losersStore - storeA until losersStore) + pits(i) = 0 + GameFinished() + } + + override def clone(): Board = { + val toReturn = new Board(0) + for (i <- toReturn.pits.indices) + toReturn.pits(i) = pits(i) + toReturn + } +} + + diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/InvalidSenderException.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/InvalidSenderException.scala new file mode 100644 index 0000000..5be50c6 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/InvalidSenderException.scala @@ -0,0 +1,5 @@ +package GameObjects.Utilities + +class InvalidSenderException(message: String) extends Exception { + +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Main.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Main.scala new file mode 100644 index 0000000..6d4b29b --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Main.scala @@ -0,0 +1,9 @@ +package GameObjects.Utilities + +import Actors.Server +import akka.actor.{ActorRef, ActorSystem, Props} + +object Main extends App { + val system = ActorSystem() + val server: ActorRef = system.actorOf(Props(classOf[Server], 10 : Long)) +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/PlayerPosition.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/PlayerPosition.scala new file mode 100644 index 0000000..6acbea9 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/PlayerPosition.scala @@ -0,0 +1,14 @@ +package GameObjects.Utilities + +sealed trait PlayerPosition { + def opponent : PlayerPosition +} +case class PlayerUpper() extends PlayerPosition { + override def opponent: PlayerPosition = PlayerLower() +} +case class PlayerLower() extends PlayerPosition { + override def opponent: PlayerPosition = PlayerUpper() +} +case class GameFinished() extends PlayerPosition { + override def opponent: PlayerPosition = ??? +} \ No newline at end of file diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Timer.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Timer.scala new file mode 100644 index 0000000..b7e5b93 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Timer.scala @@ -0,0 +1,12 @@ +package GameObjects.Utilities + +class Timer { + var lastRestart: Long = -1 + + def getTimeMillis: Long = { + if (lastRestart == -1) 0 + else System.currentTimeMillis() - lastRestart + } + def getTimeSeconds: Long = getTimeMillis / 1000 + def restart(): Unit = lastRestart = System.currentTimeMillis() +} diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/Output/ConsoleOutputTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/Output/ConsoleOutputTest.scala new file mode 100644 index 0000000..5b9b892 --- /dev/null +++ b/lista10-kalahaGame/src/test/scala/GameObjects/Output/ConsoleOutputTest.scala @@ -0,0 +1,12 @@ +package GameObjects.Outputs + +import GameObjects.Utilities.Board +import org.scalatest.matchers.should._ + +class ConsoleOutputTest extends org.scalatest.FunSuite with Matchers{ + val printer = new ConsoleOutput(new Board(6)) + test("printGame no throws test") { + noException should be thrownBy printer.printGame() + } + +} diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala new file mode 100644 index 0000000..8441e91 --- /dev/null +++ b/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala @@ -0,0 +1,126 @@ +package GameObjects.Utilities + +import GameObjects.Outputs.ConsoleOutput +import org.scalatest._ + +class BoardTest extends FunSuite { + + val seedsInPit = 6 + val board = new Board(seedsInPit) + + test("Board should generate specified number of seeds in each house") { + assert(board.playerUpperPitHouseValue(0) == seedsInPit) + assert(board.playerUpperPitHouseValue(0) == seedsInPit) + assert(board.playerLowerScore == 0) + assert(board.playerUpperScore == 0) + } + + test("simple PlayerLower move pits value test") { + board.move(3, PlayerLower()) + assert(board.playerLowerPitHouseValue(0) == 6) + assert(board.playerLowerPitHouseValue(1) == 6) + assert(board.playerLowerPitHouseValue(2) == 6) + + assert(board.playerLowerPitHouseValue(3) == 0) + + assert(board.playerLowerPitHouseValue(4) == 7) + assert(board.playerLowerPitHouseValue(5) == 7) + + assert(board.playerUpperPitHouseValue(0) == 7) + assert(board.playerUpperPitHouseValue(1) == 7) + assert(board.playerUpperPitHouseValue(2) == 7) + + assert(board.playerUpperPitHouseValue(3) == 6) + assert(board.playerUpperPitHouseValue(4) == 6) + assert(board.playerUpperPitHouseValue(5) == 6) + } + + test("simple PlayerLower move player's score test") { + assert(board.playerLowerScore == 1) + assert(board.playerUpperScore == 0) + } + + test("Illegal move throws test") { + assertThrows[IllegalArgumentException] { + board.move(-1, PlayerLower()) + } + assertThrows[IllegalArgumentException] { + board.move(-1, PlayerUpper()) + } + assertThrows[IllegalArgumentException] { + board.move(6, PlayerLower()) + } + assertThrows[IllegalArgumentException] { + board.move(6, PlayerUpper()) + } + } + + test("players pits test") { + val patternUpper = Array(board.playerUpperPitHouseValue(0), board.playerUpperPitHouseValue(1), board.playerUpperPitHouseValue(2), + board.playerUpperPitHouseValue(3), board.playerUpperPitHouseValue(4), board.playerUpperPitHouseValue(5)) + assert(patternUpper sameElements board.playerUpperPits) + val patternLower = Array(board.playerLowerPitHouseValue(0), board.playerLowerPitHouseValue(1), board.playerLowerPitHouseValue(2), + board.playerLowerPitHouseValue(3), board.playerLowerPitHouseValue(4), board.playerLowerPitHouseValue(5)) + assert(patternLower sameElements board.playerLowerPits) + } + + test("go through house test") { + board.move(5, PlayerUpper()) + assert(board.playerUpperScore == 1) + } + + test("taking opponents seeds test") { + board.move(4, PlayerLower()) + board.move(0, PlayerUpper()) + board.move(3, PlayerLower()) + assert(board.playerLowerPitHouseValue(4) == 0) + assert(board.playerUpperPitHouseValue(1) == 0) + assert(board.playerLowerScore == 12) + } + + test("game finished last seed in player's store test") { + val board = makeGameToFinishInOneTurn() + assert(GameFinished() == board.move(5, PlayerLower())) + assert(36 == board.playerLowerScore) + assert(0 == board.playerUpperScore) + } + + def makeGameToFinishInOneTurn(): Board = { + val board = new Board(3) + val player = PlayerLower() + board.move(3, player) + board.move(0, player) + board.move(1, player) + board.move(2, player) + board.move(3, player) + board.move(4, player) + board + } + + test("normal switching player test") { + val board = new Board(6) + assert(PlayerUpper() == board.move(1, PlayerLower())) + assert(PlayerLower() == board.move(1, PlayerUpper())) + } + test("switching player after taking seeds test") { + val board = new Board(1) + board.move(1, PlayerLower()) + board.move(1, PlayerUpper()) + assert(PlayerUpper() == board.move(0, PlayerLower())) + assert(PlayerLower() == board.move(0, PlayerUpper())) + } + + test("no switch player after seed into store move") { + val board = new Board(6) + assert(PlayerLower() == board.move(0, PlayerLower())) + assert(PlayerUpper() == board.move(0, PlayerUpper())) + } + + test("clone test") { + val cloned = board.clone() + assert(board.playerLowerScore == cloned.playerLowerScore) + assert(board.playerUpperScore == cloned.playerUpperScore) + assert(board.playerLowerPits sameElements cloned.playerLowerPits) + assert(board.playerUpperPits sameElements cloned.playerUpperPits) + } +} From 30c9202243461969166903575b8c96f197a00bd5 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Thu, 23 Jan 2020 09:32:58 +0100 Subject: [PATCH 13/24] interaction with user improved --- .../src/main/scala/Actors/GameMenager.scala | 28 +++++++++++++++++++ .../src/main/scala/Actors/Server.scala | 26 +++++++++-------- .../{Ai => AiAlgorithms}/HumanPlayer.scala | 0 .../{Ai => AiAlgorithms}/MoveDecider.scala | 0 4 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 lista10-kalahaGame/src/main/scala/Actors/GameMenager.scala rename lista10-kalahaGame/src/main/scala/GameObjects/{Ai => AiAlgorithms}/HumanPlayer.scala (100%) rename lista10-kalahaGame/src/main/scala/GameObjects/{Ai => AiAlgorithms}/MoveDecider.scala (100%) diff --git a/lista10-kalahaGame/src/main/scala/Actors/GameMenager.scala b/lista10-kalahaGame/src/main/scala/Actors/GameMenager.scala new file mode 100644 index 0000000..4ddc25e --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/Actors/GameMenager.scala @@ -0,0 +1,28 @@ +package Actors + +import GameObjects.AI.{AiAlgorithm, HumanPlayer} +import GameObjects.Utilities.{Board, GameFinished, PlayerLower, PlayerUpper} +import akka.actor.{Actor, Props} + +class GameMenager extends Actor{ + setUp() + private def setUp(): Unit = { + val board = new Board(6) + println("playerA: (h/ai) or end to finish") + val playerAIn = scala.io.StdIn.readLine() + if (playerAIn == "end") context.system.terminate() + else { + val playerA = if (playerAIn == "h") new HumanPlayer(board) else new AiAlgorithm(board, PlayerUpper(), 1) + println("playerB: (h/ai)") + val playerBIn = scala.io.StdIn.readLine() + val playerB = if (playerBIn == "h") new HumanPlayer(board) else new AiAlgorithm(board, PlayerLower(), 1) + context.actorOf(Props(classOf[Server], playerA, playerB, 10: Long)) + } + } + + override def receive: Receive = { + case GameFinished() => + println("Game finished") + setUp() + } +} diff --git a/lista10-kalahaGame/src/main/scala/Actors/Server.scala b/lista10-kalahaGame/src/main/scala/Actors/Server.scala index f239f26..669e8ff 100644 --- a/lista10-kalahaGame/src/main/scala/Actors/Server.scala +++ b/lista10-kalahaGame/src/main/scala/Actors/Server.scala @@ -1,19 +1,20 @@ package Actors +import akka.actor.{Actor, ActorRef, Props} import Actors.Player.{MakeMove, Move} import Actors.Server.BadMove -import GameObjects.AI.HumanPlayer -import GameObjects.Utilities.{Board, GameFinished, InvalidSenderException, PlayerLower, PlayerPosition, PlayerUpper, Timer} -import akka.actor.{Actor, ActorRef, Props} +import GameObjects.AI.MoveDecider +import GameObjects.Utilities._ -class Server(private val timeForTurn : Long = 10) extends Actor{ +class Server(playerA: MoveDecider, playerB: MoveDecider, private val timeForTurn: Long = 10) extends Actor { val timer = new Timer() val board = new Board(6) - val upperChild: ActorRef = context.actorOf(Props(classOf[Player], new HumanPlayer(board))) - val lowerChild: ActorRef = context.actorOf(Props(classOf[Player], new HumanPlayer(board))) + val upperChild: ActorRef = context.actorOf(Props(classOf[Player], playerA)) + val lowerChild: ActorRef = context.actorOf(Props(classOf[Player], playerB)) upperChild ! MakeMove() + override def receive: Receive = { - case turn : Move => + case turn: Move => try { if (timer.getTimeSeconds < timeForTurn) { if (sender() == upperChild) { @@ -25,19 +26,20 @@ class Server(private val timeForTurn : Long = 10) extends Actor{ } else throw new InvalidSenderException("Unknown sender") timer.restart() } - else println("time out") + else context.parent ! GameFinished() } catch { - case e : IllegalArgumentException => { + case e: IllegalArgumentException => { println("here") sender() ! BadMove() } } case GameFinished() => { - println("Game Finished!") + context.parent ! GameFinished() } } - private def findDestination(sendTo : PlayerPosition) ={ + + private def findDestination(sendTo: PlayerPosition) = { sendTo match { case PlayerUpper() => upperChild case PlayerLower() => lowerChild @@ -47,5 +49,7 @@ class Server(private val timeForTurn : Long = 10) extends Actor{ } object Server { + case class BadMove() + } \ No newline at end of file diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Ai/HumanPlayer.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/HumanPlayer.scala similarity index 100% rename from lista10-kalahaGame/src/main/scala/GameObjects/Ai/HumanPlayer.scala rename to lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/HumanPlayer.scala diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Ai/MoveDecider.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/MoveDecider.scala similarity index 100% rename from lista10-kalahaGame/src/main/scala/GameObjects/Ai/MoveDecider.scala rename to lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/MoveDecider.scala From 2f99e0550cb576fb6edd5da8106873258d024ff5 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Thu, 23 Jan 2020 14:32:56 +0100 Subject: [PATCH 14/24] Ai works --- .../{GameMenager.scala => GameManager.scala} | 13 ++-- .../src/main/scala/Actors/Player.scala | 6 +- .../src/main/scala/Actors/Server.scala | 26 ++++--- .../AiAlgorithms/AiAlgorithm.scala | 71 +++++++++++++++++++ .../{Ai => AiAlgorithms}/Tree.scala | 0 .../GameObjects/Output/ConsoleOutput.scala | 3 +- .../scala/GameObjects/Output/NoOutput.scala | 13 ++++ .../scala/GameObjects/Utilities/Board.scala | 34 +++++++-- .../scala/GameObjects/Utilities/Main.scala | 4 +- .../Utilities/PlayerPosition.scala | 6 +- .../AiAlgorithms/AiAlgorithmTest.scala | 71 +++++++++++++++++++ .../Output/ConsoleOutputTest.scala | 1 - .../GameObjects/Utilities/BoardTest.scala | 61 ++++++++-------- 13 files changed, 250 insertions(+), 59 deletions(-) rename lista10-kalahaGame/src/main/scala/Actors/{GameMenager.scala => GameManager.scala} (62%) create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/AiAlgorithm.scala rename lista10-kalahaGame/src/main/scala/GameObjects/{Ai => AiAlgorithms}/Tree.scala (100%) create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/Output/NoOutput.scala create mode 100644 lista10-kalahaGame/src/test/scala/GameObjects/AiAlgorithms/AiAlgorithmTest.scala diff --git a/lista10-kalahaGame/src/main/scala/Actors/GameMenager.scala b/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala similarity index 62% rename from lista10-kalahaGame/src/main/scala/Actors/GameMenager.scala rename to lista10-kalahaGame/src/main/scala/Actors/GameManager.scala index 4ddc25e..e0fcd81 100644 --- a/lista10-kalahaGame/src/main/scala/Actors/GameMenager.scala +++ b/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala @@ -1,10 +1,12 @@ package Actors import GameObjects.AI.{AiAlgorithm, HumanPlayer} +import GameObjects.Output.NoOutput +import GameObjects.Outputs.ConsoleOutput import GameObjects.Utilities.{Board, GameFinished, PlayerLower, PlayerUpper} import akka.actor.{Actor, Props} -class GameMenager extends Actor{ +class GameManager extends Actor{ setUp() private def setUp(): Unit = { val board = new Board(6) @@ -12,17 +14,18 @@ class GameMenager extends Actor{ val playerAIn = scala.io.StdIn.readLine() if (playerAIn == "end") context.system.terminate() else { - val playerA = if (playerAIn == "h") new HumanPlayer(board) else new AiAlgorithm(board, PlayerUpper(), 1) + val playerA = if (playerAIn == "h") new HumanPlayer(board) else new AiAlgorithm(board, PlayerUpper(), 2) println("playerB: (h/ai)") val playerBIn = scala.io.StdIn.readLine() val playerB = if (playerBIn == "h") new HumanPlayer(board) else new AiAlgorithm(board, PlayerLower(), 1) - context.actorOf(Props(classOf[Server], playerA, playerB, 10: Long)) + val serverOut = if (playerAIn == playerBIn == "ai") new ConsoleOutput(board) else NoOutput() + context.actorOf(Props(classOf[Server], playerA, playerB, board, 10: Long, new ConsoleOutput(board))) } } override def receive: Receive = { - case GameFinished() => - println("Game finished") + case GameFinished(string) => + println("Game finished " + string) setUp() } } diff --git a/lista10-kalahaGame/src/main/scala/Actors/Player.scala b/lista10-kalahaGame/src/main/scala/Actors/Player.scala index 15c49f4..03e0cdb 100644 --- a/lista10-kalahaGame/src/main/scala/Actors/Player.scala +++ b/lista10-kalahaGame/src/main/scala/Actors/Player.scala @@ -7,8 +7,10 @@ import akka.actor.Actor class Player(val playerDecider: MoveDecider) extends Actor { override def receive: Receive = { - case MakeMove() => makeMove() - case BadMove() => makeMove() + case MakeMove() => + makeMove() + case BadMove() => + makeMove() } diff --git a/lista10-kalahaGame/src/main/scala/Actors/Server.scala b/lista10-kalahaGame/src/main/scala/Actors/Server.scala index 669e8ff..892133b 100644 --- a/lista10-kalahaGame/src/main/scala/Actors/Server.scala +++ b/lista10-kalahaGame/src/main/scala/Actors/Server.scala @@ -4,11 +4,12 @@ import akka.actor.{Actor, ActorRef, Props} import Actors.Player.{MakeMove, Move} import Actors.Server.BadMove import GameObjects.AI.MoveDecider +import GameObjects.Output.NoOutput +import GameObjects.Outputs.Output import GameObjects.Utilities._ -class Server(playerA: MoveDecider, playerB: MoveDecider, private val timeForTurn: Long = 10) extends Actor { +class Server(playerA: MoveDecider, playerB: MoveDecider, private val board: Board, private val timeForTurn: Long = 10, private val serverOutput: Output = NoOutput()) extends Actor { val timer = new Timer() - val board = new Board(6) val upperChild: ActorRef = context.actorOf(Props(classOf[Player], playerA)) val lowerChild: ActorRef = context.actorOf(Props(classOf[Player], playerB)) upperChild ! MakeMove() @@ -16,34 +17,39 @@ class Server(playerA: MoveDecider, playerB: MoveDecider, private val timeForTurn override def receive: Receive = { case turn: Move => try { + serverOutput.printGame() if (timer.getTimeSeconds < timeForTurn) { if (sender() == upperChild) { val destination = board.move(turn.house, PlayerUpper()) - findDestination(destination) ! MakeMove() + if (destination.isInstanceOf[GameFinished]) context.parent ! GameFinished("player A won") + else findDestination(destination) ! MakeMove() } else if (sender() == lowerChild) { val destination = board.move(turn.house, PlayerLower()) - findDestination(destination) ! MakeMove() - } else throw new InvalidSenderException("Unknown sender") + if (destination.isInstanceOf[GameFinished]) context.parent ! GameFinished("player B won") + else findDestination(destination) ! MakeMove() + } + else throw new InvalidSenderException("Unknown sender") timer.restart() } - else context.parent ! GameFinished() + else context.parent ! GameFinished("time out") } catch { case e: IllegalArgumentException => { - println("here") + println(e.getMessage) + println(board.pits.toList) + println(e.getStackTrace.toList) sender() ! BadMove() } } - case GameFinished() => { + case GameFinished(string) => { context.parent ! GameFinished() } } - private def findDestination(sendTo: PlayerPosition) = { + private def findDestination(sendTo: PlayerPosition): ActorRef = { sendTo match { case PlayerUpper() => upperChild case PlayerLower() => lowerChild - case _ => throw new IllegalArgumentException } } } diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/AiAlgorithm.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/AiAlgorithm.scala new file mode 100644 index 0000000..a2ac833 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/AiAlgorithm.scala @@ -0,0 +1,71 @@ +package GameObjects.AI + +import GameObjects.Utilities.{Board, GameFinished, PlayerPosition} + +class AiAlgorithm(private val gameBoard: Board, private val aisPosition: PlayerPosition, private val algorithmDepth: Int) extends MoveDecider { + private var counter = 0 + override def getMove: Int = { + val scores = new Array[Int](6) + for (i <- scores.indices) { + if (gameBoard.playerPit(aisPosition, i) != 0) + scores(i) = countPlayersMoveScore(gameBoard.clone(), algorithmDepth, i) + else scores(i) = Int.MinValue + } + scores.indexOf(scores.max) + } + + private def countPlayersMoveScore(board: Board, depth: Int, houseToMove: Int): Int = { + val playerToMakeNextMove = board.move(houseToMove, aisPosition) + if (playerToMakeNextMove == GameFinished()) Int.MaxValue + else { + val nextFunction = menageNextMove(playerToMakeNextMove) + val scores = new Array[Int](6) + for (i <- scores.indices) { + if (board.playerPit(playerToMakeNextMove, i) != 0) + scores(i) = nextFunction(board.clone(), depth, i) + else scores(i) = Int.MinValue + } + scores.max + } + } + + private def countOpponentsMoveScore(board: Board, depth: Int, houseToMove: Int): Int = { + val playerToMakeNextMove = board.move(houseToMove, aisPosition.opponent) + if (depth == 0) { + board.playerScore(aisPosition) - board.playerScore(aisPosition.opponent) + } + else { + if (playerToMakeNextMove == GameFinished()) Int.MinValue + 1 + else { + val nextFunction = menageNextMove(playerToMakeNextMove) + val newDepth = if (playerToMakeNextMove == aisPosition) depth - 1 else depth + val scores = new Array[Int](6) + for (i <- scores.indices) { + if (board.playerPit(playerToMakeNextMove, i) != 0) + scores(i) = nextFunction(board.clone(), newDepth, i) + else scores(i) = Int.MaxValue + } + scores.min + } + } + } + + private def menageNextMove(playerPosition: PlayerPosition): (Board, Int, Int) => Int = { + if (playerPosition == aisPosition) countPlayersMoveScore + else countOpponentsMoveScore + } + + /*private def prepareMovesValue(function: (Board, Int, Int) => Int, depth: Int, player : PlayerPosition, board : Board): Seq[Int] = { + counter += 1 + println(counter) + val scores = new Array[Int](6) + for (i <- scores.indices) { + if (board.playerPit(player, i) != 0) + scores(i) = function(board.clone(), depth, i) + } + scores + }*/ + + + override def badMoveInform(message: String): Unit = println("ai missed: " + message) +} \ No newline at end of file diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Ai/Tree.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/Tree.scala similarity index 100% rename from lista10-kalahaGame/src/main/scala/GameObjects/Ai/Tree.scala rename to lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/Tree.scala diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Output/ConsoleOutput.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Output/ConsoleOutput.scala index bdfba68..50e8786 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/Output/ConsoleOutput.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Output/ConsoleOutput.scala @@ -10,9 +10,10 @@ class ConsoleOutput(private val board: Board) extends Output { uppers.foldRight()((x, Unit) => (print(x + " "))) //TODO must be better method println() for (i <- 0 to 5) - print(board.playerLowerPitHouseValue(i) + " ") + print(board.playerLowerHouseValue(i) + " ") println(s"end zone: (${board.playerLowerScore})") println("B") + println("to move " + board.toMove) } override def putMessage(message: String): Unit = println("message") diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Output/NoOutput.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Output/NoOutput.scala new file mode 100644 index 0000000..54c8bf4 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Output/NoOutput.scala @@ -0,0 +1,13 @@ +package GameObjects.Output + +import GameObjects.Outputs.Output + +class NoOutput extends Output{ + override def printGame(): Unit = () + + override def putMessage(message: String): Unit = () +} + +object NoOutput { + def apply(): NoOutput = new NoOutput() +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala index 58c28e2..1f3772b 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala @@ -1,22 +1,23 @@ package GameObjects.Utilities class Board(seedsInPit: Int) { - private val pits: Array[Int] = Array.fill(14) { + val pits: Array[Int] = Array.fill(14) { seedsInPit } + var toMove : PlayerPosition = PlayerUpper() private val storeA = 6 private val storeB = 13 pits(storeA) = 0 pits(storeB) = 0 - def playerUpperPitHouseValue(index: Int): Int = { + def playerUpperHouseValue(index: Int): Int = { require(index >= 0 && index < storeA) pits(index + 7) } def playerUpperPits: Array[Int] = pits.slice(storeA + 1, storeB) - def playersStoreIndex(playerPosition: PlayerPosition): Int = { + private def playersStoreIndex(playerPosition: PlayerPosition): Int = { playerPosition match { case PlayerUpper() => storeB case PlayerLower() => storeA @@ -24,7 +25,7 @@ class Board(seedsInPit: Int) { } } - def playerLowerPitHouseValue(index: Int): Int = { + def playerLowerHouseValue(index: Int): Int = { require(index >= 0 && index < storeA) pits(index) } @@ -39,11 +40,18 @@ class Board(seedsInPit: Int) { } } + def playerPit(player : PlayerPosition, index : Int): Int = { + player match { + case PlayerUpper() => playerUpperHouseValue(index) + case PlayerLower() => playerLowerHouseValue(index) + } + } + def playerUpperScore: Int = pits(storeB) def playerLowerScore: Int = pits(storeA) - def playerScore(player: PlayerPosition) = { + def playerScore(player: PlayerPosition): Int = { player match { case PlayerUpper() => playerUpperScore case PlayerLower() => playerLowerScore @@ -57,7 +65,7 @@ class Board(seedsInPit: Int) { val realIndex = findRealIndex(pit, playerPosition) val opponentsStore = opponentsStoreIndex(realIndex) val seeds = pits(realIndex) - if (pits(realIndex) == 0) throw new IllegalArgumentException("Cannot move from empty pit") + if (pits(realIndex) == 0) throw new IllegalArgumentException("Cannot move from empty pit number " + pit + " realIndex = " + realIndex) pits(realIndex) = 0 @scala.annotation.tailrec @@ -84,7 +92,8 @@ class Board(seedsInPit: Int) { else playerPosition.opponent } - pitVisitor(realIndex, seeds) + toMove = pitVisitor(realIndex, seeds) + toMove } private def findRealIndex(index: Int, player: PlayerPosition) = { @@ -131,4 +140,15 @@ class Board(seedsInPit: Int) { } } +object Board { + def createPosition(allPits: Seq[Int], nextMoves: PlayerPosition) : Board ={ + require(allPits.length == 14, "invalid array length " + allPits.length) + val board = new Board(1) + for (i <- board.pits.indices) + board.pits(i) = allPits(i) + board.toMove = nextMoves + board + } +} + diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Main.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Main.scala index 6d4b29b..10c0af9 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Main.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Main.scala @@ -1,9 +1,9 @@ package GameObjects.Utilities -import Actors.Server +import Actors.{GameManager, Server} import akka.actor.{ActorRef, ActorSystem, Props} object Main extends App { val system = ActorSystem() - val server: ActorRef = system.actorOf(Props(classOf[Server], 10 : Long)) + val server: ActorRef = system.actorOf(Props[GameManager]) } diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/PlayerPosition.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/PlayerPosition.scala index 6acbea9..eac1470 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/PlayerPosition.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/PlayerPosition.scala @@ -5,10 +5,14 @@ sealed trait PlayerPosition { } case class PlayerUpper() extends PlayerPosition { override def opponent: PlayerPosition = PlayerLower() + + override def toString: String = "player lower" } case class PlayerLower() extends PlayerPosition { override def opponent: PlayerPosition = PlayerUpper() + + override def toString: String = "player upper" } -case class GameFinished() extends PlayerPosition { +case class GameFinished(val message : String = "") extends PlayerPosition { override def opponent: PlayerPosition = ??? } \ No newline at end of file diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/AiAlgorithms/AiAlgorithmTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/AiAlgorithms/AiAlgorithmTest.scala new file mode 100644 index 0000000..e0844fe --- /dev/null +++ b/lista10-kalahaGame/src/test/scala/GameObjects/AiAlgorithms/AiAlgorithmTest.scala @@ -0,0 +1,71 @@ +package GameObjects.AiAlgorithms + +import GameObjects.AI.AiAlgorithm +import GameObjects.Outputs.ConsoleOutput +import GameObjects.Utilities.{Board, PlayerLower, PlayerUpper} + +class AiAlgorithmTest extends org.scalatest.FunSuite { + test("win in one move test") { + val board = new Board(1) + val player = PlayerLower() + val ai = new AiAlgorithm(board, player, 0) + for (i <- 0 until 5) { + board.move(i, player) + } + assert(ai.getMove == 5) + } + + test("should chose go into empty house") { + val board = new Board(2) + val player = PlayerLower() + val ai = new AiAlgorithm(board, player, 0) + board.move(1, player) + board.move(2, player) + board.move(3, player) + board.move(4, player) + assert(ai.getMove == 0) + } + + test("Opening test as lower") { + val ai = new AiAlgorithm(new Board(1), PlayerLower(), 0) + assert(ai.getMove == 5) + } + + test("opening test as upper") { + val board = new Board(3) + val player = PlayerUpper() + val ai = new AiAlgorithm(board, player, 0) + board.move(1, player.opponent) + assert(ai.getMove == 3) + } + + test("deeper ai test") { + val board = new Board(3) + val player = PlayerUpper() + val ai = new AiAlgorithm(board, player, 1) + board.move(1, player.opponent) + assert(ai.getMove == 3) + } + + test("one option test to win") { + val player = PlayerUpper() + val position = Array(0, 0, 0, 2, 0, 0, 36, 0, 0, 0, 0, 0, 1, 33) + val board = Board.createPosition(position, player) + val printer = new ConsoleOutput(board) + printer.printGame() + val ai = new AiAlgorithm(board, player, 1) + assert(ai.getMove == 5) + board.move(5, player) + } + + test("one option test, can't win") { + val player = PlayerLower() + val position = Array(0, 0, 0, 2, 0, 0, 36, 0, 0, 0, 0, 0, 1, 33) + val board = Board.createPosition(position, player) + val printer = new ConsoleOutput(board) + printer.printGame() + val ai = new AiAlgorithm(board, player, 1) + assert(ai.getMove == 3) + } + +} diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/Output/ConsoleOutputTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/Output/ConsoleOutputTest.scala index 5b9b892..987d571 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/Output/ConsoleOutputTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/Output/ConsoleOutputTest.scala @@ -8,5 +8,4 @@ class ConsoleOutputTest extends org.scalatest.FunSuite with Matchers{ test("printGame no throws test") { noException should be thrownBy printer.printGame() } - } diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala index 8441e91..8b9ad4e 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala @@ -1,6 +1,5 @@ package GameObjects.Utilities -import GameObjects.Outputs.ConsoleOutput import org.scalatest._ class BoardTest extends FunSuite { @@ -9,30 +8,30 @@ class BoardTest extends FunSuite { val board = new Board(seedsInPit) test("Board should generate specified number of seeds in each house") { - assert(board.playerUpperPitHouseValue(0) == seedsInPit) - assert(board.playerUpperPitHouseValue(0) == seedsInPit) + assert(board.playerUpperHouseValue(0) == seedsInPit) + assert(board.playerUpperHouseValue(0) == seedsInPit) assert(board.playerLowerScore == 0) assert(board.playerUpperScore == 0) } test("simple PlayerLower move pits value test") { board.move(3, PlayerLower()) - assert(board.playerLowerPitHouseValue(0) == 6) - assert(board.playerLowerPitHouseValue(1) == 6) - assert(board.playerLowerPitHouseValue(2) == 6) + assert(board.playerLowerHouseValue(0) == 6) + assert(board.playerLowerHouseValue(1) == 6) + assert(board.playerLowerHouseValue(2) == 6) - assert(board.playerLowerPitHouseValue(3) == 0) + assert(board.playerLowerHouseValue(3) == 0) - assert(board.playerLowerPitHouseValue(4) == 7) - assert(board.playerLowerPitHouseValue(5) == 7) + assert(board.playerLowerHouseValue(4) == 7) + assert(board.playerLowerHouseValue(5) == 7) - assert(board.playerUpperPitHouseValue(0) == 7) - assert(board.playerUpperPitHouseValue(1) == 7) - assert(board.playerUpperPitHouseValue(2) == 7) + assert(board.playerUpperHouseValue(0) == 7) + assert(board.playerUpperHouseValue(1) == 7) + assert(board.playerUpperHouseValue(2) == 7) - assert(board.playerUpperPitHouseValue(3) == 6) - assert(board.playerUpperPitHouseValue(4) == 6) - assert(board.playerUpperPitHouseValue(5) == 6) + assert(board.playerUpperHouseValue(3) == 6) + assert(board.playerUpperHouseValue(4) == 6) + assert(board.playerUpperHouseValue(5) == 6) } test("simple PlayerLower move player's score test") { @@ -56,15 +55,15 @@ class BoardTest extends FunSuite { } test("players pits test") { - val patternUpper = Array(board.playerUpperPitHouseValue(0), board.playerUpperPitHouseValue(1), board.playerUpperPitHouseValue(2), - board.playerUpperPitHouseValue(3), board.playerUpperPitHouseValue(4), board.playerUpperPitHouseValue(5)) + val patternUpper = Array(board.playerUpperHouseValue(0), board.playerUpperHouseValue(1), board.playerUpperHouseValue(2), + board.playerUpperHouseValue(3), board.playerUpperHouseValue(4), board.playerUpperHouseValue(5)) assert(patternUpper sameElements board.playerUpperPits) - val patternLower = Array(board.playerLowerPitHouseValue(0), board.playerLowerPitHouseValue(1), board.playerLowerPitHouseValue(2), - board.playerLowerPitHouseValue(3), board.playerLowerPitHouseValue(4), board.playerLowerPitHouseValue(5)) + val patternLower = Array(board.playerLowerHouseValue(0), board.playerLowerHouseValue(1), board.playerLowerHouseValue(2), + board.playerLowerHouseValue(3), board.playerLowerHouseValue(4), board.playerLowerHouseValue(5)) assert(patternLower sameElements board.playerLowerPits) } - test("go through house test") { + test("go store house test") { board.move(5, PlayerUpper()) assert(board.playerUpperScore == 1) } @@ -73,8 +72,8 @@ class BoardTest extends FunSuite { board.move(4, PlayerLower()) board.move(0, PlayerUpper()) board.move(3, PlayerLower()) - assert(board.playerLowerPitHouseValue(4) == 0) - assert(board.playerUpperPitHouseValue(1) == 0) + assert(board.playerLowerHouseValue(4) == 0) + assert(board.playerUpperHouseValue(1) == 0) assert(board.playerLowerScore == 12) } @@ -86,15 +85,9 @@ class BoardTest extends FunSuite { } def makeGameToFinishInOneTurn(): Board = { - val board = new Board(3) val player = PlayerLower() - board.move(3, player) - board.move(0, player) - board.move(1, player) - board.move(2, player) - board.move(3, player) - board.move(4, player) - board + val position = Array(0, 0, 0, 0, 0, 8, 7, 5, 4, 1, 4, 4, 3, 0) + Board.createPosition(position, player) } test("normal switching player test") { @@ -123,4 +116,12 @@ class BoardTest extends FunSuite { assert(board.playerLowerPits sameElements cloned.playerLowerPits) assert(board.playerUpperPits sameElements cloned.playerUpperPits) } + + test("empty house move attempt should throw") { + val board = new Board(1) + board.move(0, PlayerLower()) + assertThrows[IllegalArgumentException] { + board.move(0, PlayerLower()) + } + } } From 002bf228d9c922eed6496f3d39c8957d5e608a5c Mon Sep 17 00:00:00 2001 From: mprzymus Date: Mon, 3 May 2021 13:14:37 +0200 Subject: [PATCH 15/24] refactoring, bug fixing --- .../src/main/scala/Actors/GameManager.scala | 8 ++-- .../src/main/scala/Actors/Player.scala | 1 - .../src/main/scala/Actors/Server.scala | 19 +++++---- .../{AiAlgorithms => AI}/AiAlgorithm.scala | 1 - .../{AiAlgorithms => AI}/HumanPlayer.scala | 1 - .../{AiAlgorithms => AI}/MoveDecider.scala | 0 .../{AiAlgorithms => AI}/Tree.scala | 0 .../GameObjects/Output/ConsoleOutput.scala | 20 ---------- .../GameObjects/Outputs/ConsoleOutput.scala | 39 +++++++++++++++++++ .../{Output => Outputs}/NoOutput.scala | 4 +- .../{Output => Outputs}/Output.scala | 0 .../scala/GameObjects/Utilities/Board.scala | 2 +- .../Utilities/PlayerPosition.scala | 4 +- .../AiAlgorithmTest.scala | 2 +- .../ConsoleOutputTest.scala | 0 .../GameObjects/Utilities/BoardTest.scala | 36 +++++++++++++++++ 16 files changed, 93 insertions(+), 44 deletions(-) rename lista10-kalahaGame/src/main/scala/GameObjects/{AiAlgorithms => AI}/AiAlgorithm.scala (98%) rename lista10-kalahaGame/src/main/scala/GameObjects/{AiAlgorithms => AI}/HumanPlayer.scala (94%) rename lista10-kalahaGame/src/main/scala/GameObjects/{AiAlgorithms => AI}/MoveDecider.scala (100%) rename lista10-kalahaGame/src/main/scala/GameObjects/{AiAlgorithms => AI}/Tree.scala (100%) delete mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/Output/ConsoleOutput.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/Outputs/ConsoleOutput.scala rename lista10-kalahaGame/src/main/scala/GameObjects/{Output => Outputs}/NoOutput.scala (75%) rename lista10-kalahaGame/src/main/scala/GameObjects/{Output => Outputs}/Output.scala (100%) rename lista10-kalahaGame/src/test/scala/GameObjects/{AiAlgorithms => AI}/AiAlgorithmTest.scala (98%) rename lista10-kalahaGame/src/test/scala/GameObjects/{Output => Outputs}/ConsoleOutputTest.scala (100%) diff --git a/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala b/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala index e0fcd81..0e06cb2 100644 --- a/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala +++ b/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala @@ -1,7 +1,6 @@ package Actors import GameObjects.AI.{AiAlgorithm, HumanPlayer} -import GameObjects.Output.NoOutput import GameObjects.Outputs.ConsoleOutput import GameObjects.Utilities.{Board, GameFinished, PlayerLower, PlayerUpper} import akka.actor.{Actor, Props} @@ -9,7 +8,7 @@ import akka.actor.{Actor, Props} class GameManager extends Actor{ setUp() private def setUp(): Unit = { - val board = new Board(6) + val board = new Board(1) println("playerA: (h/ai) or end to finish") val playerAIn = scala.io.StdIn.readLine() if (playerAIn == "end") context.system.terminate() @@ -18,8 +17,9 @@ class GameManager extends Actor{ println("playerB: (h/ai)") val playerBIn = scala.io.StdIn.readLine() val playerB = if (playerBIn == "h") new HumanPlayer(board) else new AiAlgorithm(board, PlayerLower(), 1) - val serverOut = if (playerAIn == playerBIn == "ai") new ConsoleOutput(board) else NoOutput() - context.actorOf(Props(classOf[Server], playerA, playerB, board, 10: Long, new ConsoleOutput(board))) + val output = new ConsoleOutput(board) + output.printGame() + context.actorOf(Props(classOf[Server], playerA, playerB, board, Long.MaxValue: Long, output)) } } diff --git a/lista10-kalahaGame/src/main/scala/Actors/Player.scala b/lista10-kalahaGame/src/main/scala/Actors/Player.scala index 03e0cdb..c2c225a 100644 --- a/lista10-kalahaGame/src/main/scala/Actors/Player.scala +++ b/lista10-kalahaGame/src/main/scala/Actors/Player.scala @@ -11,7 +11,6 @@ class Player(val playerDecider: MoveDecider) extends Actor { makeMove() case BadMove() => makeMove() - } private def makeMove(): Unit = { diff --git a/lista10-kalahaGame/src/main/scala/Actors/Server.scala b/lista10-kalahaGame/src/main/scala/Actors/Server.scala index 892133b..8ee00d3 100644 --- a/lista10-kalahaGame/src/main/scala/Actors/Server.scala +++ b/lista10-kalahaGame/src/main/scala/Actors/Server.scala @@ -4,7 +4,7 @@ import akka.actor.{Actor, ActorRef, Props} import Actors.Player.{MakeMove, Move} import Actors.Server.BadMove import GameObjects.AI.MoveDecider -import GameObjects.Output.NoOutput +import GameObjects.Outputs.NoOutput import GameObjects.Outputs.Output import GameObjects.Utilities._ @@ -17,33 +17,32 @@ class Server(playerA: MoveDecider, playerB: MoveDecider, private val board: Boar override def receive: Receive = { case turn: Move => try { - serverOutput.printGame() if (timer.getTimeSeconds < timeForTurn) { if (sender() == upperChild) { val destination = board.move(turn.house, PlayerUpper()) - if (destination.isInstanceOf[GameFinished]) context.parent ! GameFinished("player A won") + if (destination.isInstanceOf[GameFinished]) + context.parent ! GameFinished("player A won") else findDestination(destination) ! MakeMove() } else if (sender() == lowerChild) { val destination = board.move(turn.house, PlayerLower()) - if (destination.isInstanceOf[GameFinished]) context.parent ! GameFinished("player B won") + if (destination.isInstanceOf[GameFinished]) + context.parent ! GameFinished("player B won") else findDestination(destination) ! MakeMove() } else throw new InvalidSenderException("Unknown sender") + serverOutput.printGame() timer.restart() } else context.parent ! GameFinished("time out") } catch { - case e: IllegalArgumentException => { + case e: IllegalArgumentException => println(e.getMessage) - println(board.pits.toList) - println(e.getStackTrace.toList) + serverOutput.printGame() sender() ! BadMove() - } } - case GameFinished(string) => { + case GameFinished(_) => context.parent ! GameFinished() - } } private def findDestination(sendTo: PlayerPosition): ActorRef = { diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/AiAlgorithm.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/AiAlgorithm.scala similarity index 98% rename from lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/AiAlgorithm.scala rename to lista10-kalahaGame/src/main/scala/GameObjects/AI/AiAlgorithm.scala index a2ac833..5ba66f0 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/AiAlgorithm.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/AiAlgorithm.scala @@ -3,7 +3,6 @@ package GameObjects.AI import GameObjects.Utilities.{Board, GameFinished, PlayerPosition} class AiAlgorithm(private val gameBoard: Board, private val aisPosition: PlayerPosition, private val algorithmDepth: Int) extends MoveDecider { - private var counter = 0 override def getMove: Int = { val scores = new Array[Int](6) for (i <- scores.indices) { diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/HumanPlayer.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/HumanPlayer.scala similarity index 94% rename from lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/HumanPlayer.scala rename to lista10-kalahaGame/src/main/scala/GameObjects/AI/HumanPlayer.scala index be52b3b..e5989bf 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/HumanPlayer.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/HumanPlayer.scala @@ -6,7 +6,6 @@ import GameObjects.Utilities.Board class HumanPlayer(private val board: Board) extends MoveDecider { private val view = new ConsoleOutput(board) override def getMove: Int = { - view.printGame() scala.io.StdIn.readInt() } diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/MoveDecider.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/MoveDecider.scala similarity index 100% rename from lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/MoveDecider.scala rename to lista10-kalahaGame/src/main/scala/GameObjects/AI/MoveDecider.scala diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/Tree.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/Tree.scala similarity index 100% rename from lista10-kalahaGame/src/main/scala/GameObjects/AiAlgorithms/Tree.scala rename to lista10-kalahaGame/src/main/scala/GameObjects/AI/Tree.scala diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Output/ConsoleOutput.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Output/ConsoleOutput.scala deleted file mode 100644 index 50e8786..0000000 --- a/lista10-kalahaGame/src/main/scala/GameObjects/Output/ConsoleOutput.scala +++ /dev/null @@ -1,20 +0,0 @@ -package GameObjects.Outputs - -import GameObjects.Utilities.Board - -class ConsoleOutput(private val board: Board) extends Output { - override def printGame(): Unit = { - println("A") - print("end zone: (" + board.playerUpperScore + ") ") - val uppers = board.playerUpperPits - uppers.foldRight()((x, Unit) => (print(x + " "))) //TODO must be better method - println() - for (i <- 0 to 5) - print(board.playerLowerHouseValue(i) + " ") - println(s"end zone: (${board.playerLowerScore})") - println("B") - println("to move " + board.toMove) - } - - override def putMessage(message: String): Unit = println("message") -} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Outputs/ConsoleOutput.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Outputs/ConsoleOutput.scala new file mode 100644 index 0000000..35ac197 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Outputs/ConsoleOutput.scala @@ -0,0 +1,39 @@ +package GameObjects.Outputs + +import GameObjects.Utilities.Board + +import scala.annotation.tailrec + +class ConsoleOutput(private val board: Board) extends Output { + + def getSpacingStr(playerUpperScore: Int): String = { + @tailrec + def spacingTailRec(score: Int, acc: StringBuilder): String = { + if (score > 0) { + acc ++= " " + spacingTailRec(score - 1, acc) + } + else { + acc.toString() + } + } + spacingTailRec(playerUpperScore.toString.length + 3, new StringBuilder()) + } + + override def printGame(): Unit = { + println("A") + print("(" + board.playerUpperScore + ") ") + val uppers = board.playerUpperPits + uppers.foldRight()((x, _) => print(x + " ")) + println() + val spacingStr = getSpacingStr(board.playerUpperScore) + print(spacingStr) + for (i <- 0 to 5) + print(board.playerLowerHouseValue(i) + " ") + println(s"(${board.playerLowerScore})") + println("B") + println("to move " + board.toMove) + } + + override def putMessage(message: String): Unit = println("message") +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Output/NoOutput.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Outputs/NoOutput.scala similarity index 75% rename from lista10-kalahaGame/src/main/scala/GameObjects/Output/NoOutput.scala rename to lista10-kalahaGame/src/main/scala/GameObjects/Outputs/NoOutput.scala index 54c8bf4..e72af7f 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/Output/NoOutput.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Outputs/NoOutput.scala @@ -1,6 +1,4 @@ -package GameObjects.Output - -import GameObjects.Outputs.Output +package GameObjects.Outputs class NoOutput extends Output{ override def printGame(): Unit = () diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Output/Output.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Outputs/Output.scala similarity index 100% rename from lista10-kalahaGame/src/main/scala/GameObjects/Output/Output.scala rename to lista10-kalahaGame/src/main/scala/GameObjects/Outputs/Output.scala diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala index 1f3772b..04519d1 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala @@ -79,7 +79,7 @@ class Board(seedsInPit: Int) { if (sumPoints(playerPosition) == 0) finishGame(playerPosition) else playerPosition } - else if (samePlayers(pit, previousIndex) && pits(previousIndex) == 1) { + else if (samePlayers(realIndex, previousIndex) && pits(previousIndex) == 1) { val opposite = oppositeIndex(previousIndex) val store = playersStoreIndex(previousIndex) pits(store) += pits(opposite) diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/PlayerPosition.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/PlayerPosition.scala index eac1470..b9e01d3 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/PlayerPosition.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/PlayerPosition.scala @@ -6,12 +6,12 @@ sealed trait PlayerPosition { case class PlayerUpper() extends PlayerPosition { override def opponent: PlayerPosition = PlayerLower() - override def toString: String = "player lower" + override def toString: String = "player upper" } case class PlayerLower() extends PlayerPosition { override def opponent: PlayerPosition = PlayerUpper() - override def toString: String = "player upper" + override def toString: String = "player lower" } case class GameFinished(val message : String = "") extends PlayerPosition { override def opponent: PlayerPosition = ??? diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/AiAlgorithms/AiAlgorithmTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/AI/AiAlgorithmTest.scala similarity index 98% rename from lista10-kalahaGame/src/test/scala/GameObjects/AiAlgorithms/AiAlgorithmTest.scala rename to lista10-kalahaGame/src/test/scala/GameObjects/AI/AiAlgorithmTest.scala index e0844fe..0681809 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/AiAlgorithms/AiAlgorithmTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/AI/AiAlgorithmTest.scala @@ -1,4 +1,4 @@ -package GameObjects.AiAlgorithms +package GameObjects.AI import GameObjects.AI.AiAlgorithm import GameObjects.Outputs.ConsoleOutput diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/Output/ConsoleOutputTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/Outputs/ConsoleOutputTest.scala similarity index 100% rename from lista10-kalahaGame/src/test/scala/GameObjects/Output/ConsoleOutputTest.scala rename to lista10-kalahaGame/src/test/scala/GameObjects/Outputs/ConsoleOutputTest.scala diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala index 8b9ad4e..2515f45 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala @@ -1,5 +1,6 @@ package GameObjects.Utilities +import GameObjects.Outputs.ConsoleOutput import org.scalatest._ class BoardTest extends FunSuite { @@ -125,3 +126,38 @@ class BoardTest extends FunSuite { } } } + +class BoardTakingTest extends FunSuite with BeforeAndAfterEach { + val seedsInPit = 4 + var board : Board = _ + + override def beforeEach() { + board = new Board(seedsInPit) + } + + test("UpperTakesTest") { + val player = PlayerUpper() + performGame(player) + + assert(board.playerUpperScore == 7) + assert(board.playerUpperHouseValue(5) == 0) + assert(board.playerLowerHouseValue(0) == 0) + } + + test("LowerTakesTest") { + val player = PlayerLower() + performGame(player) + + assert(board.playerLowerScore == 7) + assert(board.playerLowerHouseValue(5) == 0) + assert(board.playerUpperHouseValue(0) == 0) + } + + private def performGame(player: PlayerPosition) = { + val output = new ConsoleOutput(board) + board.move(5, player) + output.printGame() + board.move(1, player) + output.printGame() + } +} From 1c6bcf77bdb605bdd1e1c83177d0c86e6a0d0e65 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Mon, 3 May 2021 15:47:59 +0200 Subject: [PATCH 16/24] generation as list --- .../GameObjects/AI/DepthDetermination.scala | 5 ++ .../scala/GameObjects/AI/FixedDepth.scala | 5 ++ .../GameObjects/AI/MinMaxAlgorithm.scala | 31 +++++++++++ .../GameObjects/AI/MinMaxAlgorithmTest.scala | 54 +++++++++++++++++++ .../GameObjects/Utilities/BoardTest.scala | 2 +- 5 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/AI/DepthDetermination.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/AI/FixedDepth.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/AI/MinMaxAlgorithm.scala create mode 100644 lista10-kalahaGame/src/test/scala/GameObjects/AI/MinMaxAlgorithmTest.scala diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/DepthDetermination.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/DepthDetermination.scala new file mode 100644 index 0000000..abd67ce --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/DepthDetermination.scala @@ -0,0 +1,5 @@ +package GameObjects.AI + +trait DepthDetermination { + def determineDepth: Int +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/FixedDepth.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/FixedDepth.scala new file mode 100644 index 0000000..ac4e533 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/FixedDepth.scala @@ -0,0 +1,5 @@ +package GameObjects.AI + +class FixedDepth(private val depth: Int) extends DepthDetermination { + override def determineDepth: Int = depth +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/MinMaxAlgorithm.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/MinMaxAlgorithm.scala new file mode 100644 index 0000000..123a7fd --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/MinMaxAlgorithm.scala @@ -0,0 +1,31 @@ +package GameObjects.AI + +import GameObjects.Utilities.{Board, PlayerPosition} + +class MinMaxAlgorithm(private val gameBoard: Board, private val aisPosition: PlayerPosition, private val depthDetermination: DepthDetermination) extends MoveDecider { + + def generateCombinations(determineDepth: Int): List[List[Int]] = { + def createList(depthToDo: Int, soFar: List[Int]): List[List[Int]] = { + if (depthToDo == 0) { + List(soFar.reverse) + } + else { + val toReturn: List[List[Int]] = (0 to 5) + .map(x => x :: soFar) + .toList + toReturn.flatMap(l => { + createList(depthToDo - 1, l) + }) + } + } + + createList(determineDepth, List.empty) + } + + override def getMove: Int = { + val combinations = generateCombinations(depthDetermination.determineDepth) + -1 + } + + override def badMoveInform(message: String): Unit = println("ai missed: " + message) +} diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/AI/MinMaxAlgorithmTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/AI/MinMaxAlgorithmTest.scala new file mode 100644 index 0000000..8420f76 --- /dev/null +++ b/lista10-kalahaGame/src/test/scala/GameObjects/AI/MinMaxAlgorithmTest.scala @@ -0,0 +1,54 @@ +package GameObjects.AI + +import GameObjects.Utilities.{Board, PlayerUpper} +import org.scalatest.BeforeAndAfterEach + +class MinMaxAlgorithmTest extends org.scalatest.FunSuite with BeforeAndAfterEach { + var board: Board = _ + var minMaxAlgorithm: MinMaxAlgorithm = _ + val depth = 5 + + override def beforeEach() { + board = new Board(4) + minMaxAlgorithm = new MinMaxAlgorithm(board, PlayerUpper(), new FixedDepth(depth)) + } + + test("generateCombinations test") { + val depth = 3 + val result = minMaxAlgorithm.generateCombinations(depth) + val first = result.head + val third = result(2) + val threeFourTwoIndex = Math.pow(6, depth - 1) * 3 + Math.pow(6, depth - 2) * 4 + 2 + val threeFourTwo = result(threeFourTwoIndex.toInt) + + assert(result.length == Math.pow(6, depth).toInt) + result.foreach(list => { + assert(list.length == depth) + }) + + assert(first == List(0,0,0)) + assert(third == List(0,0,2)) + assert(threeFourTwo == List(3,4,2)) + } + + test("generateCombinations depth 0 returns empty") { + val depth = 0 + val result = minMaxAlgorithm.generateCombinations(depth) + + assert(result.isEmpty) + } + + test("generateCombinations depth 1 returns number of pits") { + val depth = 1 + val result = minMaxAlgorithm.generateCombinations(depth) + var iterator = 0 + + assert(result.length == Math.pow(6, 1).toInt) + result.foreach(list => { + assert(list.length == depth) + assert(list.head == iterator) + iterator += 1 + }) + } + +} diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala index 2515f45..f883dd4 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala @@ -153,7 +153,7 @@ class BoardTakingTest extends FunSuite with BeforeAndAfterEach { assert(board.playerUpperHouseValue(0) == 0) } - private def performGame(player: PlayerPosition) = { + private def performGame(player: PlayerPosition): Unit = { val output = new ConsoleOutput(board) board.move(5, player) output.printGame() From 94be65ba1e3b81268baa61f6e88aad54c432e022 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Tue, 4 May 2021 03:12:51 +0200 Subject: [PATCH 17/24] minimax algorithm implemented --- .../GameObjects/AI/MinMaxAlgorithm.scala | 31 ------- .../src/main/scala/GameObjects/AI/Tree.scala | 6 -- .../AI/evaluation/EvaluationByResult.scala | 8 ++ .../AI/evaluation/EvaluationStrategy.scala | 7 ++ .../AI/{ => minimax}/DepthDetermination.scala | 2 +- .../AI/{ => minimax}/FixedDepth.scala | 2 +- .../AI/minimax/MinMaxAlgorithm.scala | 35 ++++++++ .../AI/minimax/MiniMaxVisitor.scala | 83 +++++++++++++++++++ .../scala/GameObjects/AI/minimax/Tree.scala | 24 ++++++ .../GameObjects/AI/minimax/TreeVisitor.scala | 5 ++ .../scala/GameObjects/Utilities/Board.scala | 13 +-- .../GameObjects/AI/MinMaxAlgorithmTest.scala | 56 ++++++++----- .../AI/minimax/MiniMaxVisitorTest.scala | 47 +++++++++++ 13 files changed, 253 insertions(+), 66 deletions(-) delete mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/AI/MinMaxAlgorithm.scala delete mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/AI/Tree.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/AI/evaluation/EvaluationByResult.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/AI/evaluation/EvaluationStrategy.scala rename lista10-kalahaGame/src/main/scala/GameObjects/AI/{ => minimax}/DepthDetermination.scala (64%) rename lista10-kalahaGame/src/main/scala/GameObjects/AI/{ => minimax}/FixedDepth.scala (78%) create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MinMaxAlgorithm.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MiniMaxVisitor.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/Tree.scala create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/TreeVisitor.scala create mode 100644 lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/MinMaxAlgorithm.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/MinMaxAlgorithm.scala deleted file mode 100644 index 123a7fd..0000000 --- a/lista10-kalahaGame/src/main/scala/GameObjects/AI/MinMaxAlgorithm.scala +++ /dev/null @@ -1,31 +0,0 @@ -package GameObjects.AI - -import GameObjects.Utilities.{Board, PlayerPosition} - -class MinMaxAlgorithm(private val gameBoard: Board, private val aisPosition: PlayerPosition, private val depthDetermination: DepthDetermination) extends MoveDecider { - - def generateCombinations(determineDepth: Int): List[List[Int]] = { - def createList(depthToDo: Int, soFar: List[Int]): List[List[Int]] = { - if (depthToDo == 0) { - List(soFar.reverse) - } - else { - val toReturn: List[List[Int]] = (0 to 5) - .map(x => x :: soFar) - .toList - toReturn.flatMap(l => { - createList(depthToDo - 1, l) - }) - } - } - - createList(determineDepth, List.empty) - } - - override def getMove: Int = { - val combinations = generateCombinations(depthDetermination.determineDepth) - -1 - } - - override def badMoveInform(message: String): Unit = println("ai missed: " + message) -} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/Tree.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/Tree.scala deleted file mode 100644 index 5aab48b..0000000 --- a/lista10-kalahaGame/src/main/scala/GameObjects/AI/Tree.scala +++ /dev/null @@ -1,6 +0,0 @@ -package GameObjects.AI - -trait Tree { - case class Node(houseToMove : Int) - case class Empty() -} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/evaluation/EvaluationByResult.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/evaluation/EvaluationByResult.scala new file mode 100644 index 0000000..9fbab8e --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/evaluation/EvaluationByResult.scala @@ -0,0 +1,8 @@ +package GameObjects.AI.evaluation +import GameObjects.Utilities.{Board, PlayerPosition} + +class EvaluationByResult extends EvaluationStrategy { + override def evaluate(player: PlayerPosition, board: Board): Int = { + board.playerScore(player) - board.playerScore(player.opponent) + } +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/evaluation/EvaluationStrategy.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/evaluation/EvaluationStrategy.scala new file mode 100644 index 0000000..7789f6c --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/evaluation/EvaluationStrategy.scala @@ -0,0 +1,7 @@ +package GameObjects.AI.evaluation + +import GameObjects.Utilities.{Board, PlayerPosition} + +trait EvaluationStrategy { + def evaluate(player: PlayerPosition, board: Board): Int +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/DepthDetermination.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/DepthDetermination.scala similarity index 64% rename from lista10-kalahaGame/src/main/scala/GameObjects/AI/DepthDetermination.scala rename to lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/DepthDetermination.scala index abd67ce..1ac5a3c 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/AI/DepthDetermination.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/DepthDetermination.scala @@ -1,4 +1,4 @@ -package GameObjects.AI +package GameObjects.AI.minimax trait DepthDetermination { def determineDepth: Int diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/FixedDepth.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/FixedDepth.scala similarity index 78% rename from lista10-kalahaGame/src/main/scala/GameObjects/AI/FixedDepth.scala rename to lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/FixedDepth.scala index ac4e533..38835c5 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/AI/FixedDepth.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/FixedDepth.scala @@ -1,4 +1,4 @@ -package GameObjects.AI +package GameObjects.AI.minimax class FixedDepth(private val depth: Int) extends DepthDetermination { override def determineDepth: Int = depth diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MinMaxAlgorithm.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MinMaxAlgorithm.scala new file mode 100644 index 0000000..cc75737 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MinMaxAlgorithm.scala @@ -0,0 +1,35 @@ +package GameObjects.AI.minimax + +import GameObjects.AI._ +import GameObjects.AI.evaluation.EvaluationByResult +import GameObjects.Utilities.{Board, PlayerPosition} + +class MinMaxAlgorithm(private val gameBoard: Board, private val aisPosition: PlayerPosition, + private val depthDetermination: DepthDetermination, private val aiAlgorithm: MiniMaxVisitor = new MiniMaxVisitor(new EvaluationByResult)) + extends MoveDecider { + + def generateCombinations(determineDepth: Int): Tree = { + def createList(depthToDo: Int, nodeValue: Int): Tree = { + if (depthToDo == 0) { + Leaf(nodeValue) + } + else { + val children = (0 to 5) + .map(x => createList(depthToDo - 1, x)) + .toList + if (nodeValue >= 0) + Node(nodeValue, children) + else Root(children) + } + } + + createList(determineDepth, -1) + } + + override def getMove: Int = { + val combinations = generateCombinations(depthDetermination.determineDepth) + aiAlgorithm.minimax(combinations, aisPosition, gameBoard) + } + + override def badMoveInform(message: String): Unit = println("ai missed: " + message) +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MiniMaxVisitor.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MiniMaxVisitor.scala new file mode 100644 index 0000000..7452d47 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MiniMaxVisitor.scala @@ -0,0 +1,83 @@ +package GameObjects.AI.minimax + +import GameObjects.AI.evaluation.EvaluationStrategy +import GameObjects.Utilities.{Board, GameFinished, PlayerPosition} + +class MiniMaxVisitor(evaluationStrategy: EvaluationStrategy) extends TreeVisitor { + private var playerPosition: PlayerPosition = _ + + override def visit(tree: Tree): Unit = ??? + + def isLegalToMove(houseToMove: Int, board: Board, player: PlayerPosition): Boolean = { + board.playerPit(player, houseToMove) != 0 + } + + def minimax(tree: Tree, player: PlayerPosition, board: Board): Int = { + tree match { + case asLeaf: Leaf => + move(player, board, asLeaf) + case asNode: Node => + if (isLegalToMove(asNode.houseToMove, board, board.toMove)) { + if (!shouldSkipTurn(player, board)) { + move(player, board, asNode) + } + else { + moveAllPossibilities(asNode, board.toMove, board) + } + } + else { + Int.MinValue + } + + case asRoot: Root => + playerPosition = player + val children = asRoot.children.map(child => minimax(child, player, board.clone())) + children.indices.maxBy(children) + } + } + + private def shouldSkipTurn(player: PlayerPosition, board: Board) = { + player != board.toMove + } + + private def move(player: PlayerPosition, board: Board, asNode: Node) = { + val nextPlayer = board.move(asNode.houseToMove, board.toMove) + if (nextPlayer == GameFinished()) { + handleGameFinished(evaluationStrategy.evaluate(player, board)) + } + else { + val nextPlayer = player.opponent + moveAllPossibilities(asNode, nextPlayer, board) + } + } + + private def moveAllPossibilities(asNode: Node, nextPlayer: PlayerPosition, board: Board) = { + val children = asNode.children.map(child => minimax(child, nextPlayer, board.clone())) + children.max * -1 // always max cause evaluation function returns value for current player + } + + private def move(player: PlayerPosition, board: Board, house: Leaf) = { + if (board.toMove != player) { + val result = evaluationStrategy.evaluate(player, board) + result + } + else if (isLegalToMove(house.houseToMove, board, player)) { + val next = board.move(house.houseToMove, board.toMove) + val result = evaluationStrategy.evaluate(player, board) + if (next == GameFinished()) { + handleGameFinished(result) + } + else { + val result = evaluationStrategy.evaluate(player, board) + result + } + } + else { + Int.MinValue + } + } + + private def handleGameFinished(result: Int) = { + result + } +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/Tree.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/Tree.scala new file mode 100644 index 0000000..59f7190 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/Tree.scala @@ -0,0 +1,24 @@ +package GameObjects.AI.minimax + +sealed trait Tree { + def getDepth: Int + def accept(treeVisitor: TreeVisitor): Unit = { + treeVisitor.visit(this) + } +} + +case class Root(children: List[Tree]) extends Tree { + override def getDepth: Int = { + 1 + children.head.getDepth + } +} + +case class Node(houseToMove: Int, children: List[Tree]) extends Tree { + override def getDepth: Int = { + 1 + children.head.getDepth + } +} + +case class Leaf(houseToMove: Int) extends Tree { + override def getDepth: Int = 0 +} \ No newline at end of file diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/TreeVisitor.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/TreeVisitor.scala new file mode 100644 index 0000000..e584383 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/TreeVisitor.scala @@ -0,0 +1,5 @@ +package GameObjects.AI.minimax + +trait TreeVisitor { + def visit(tree: Tree) +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala index 04519d1..8c06368 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala @@ -10,11 +10,6 @@ class Board(seedsInPit: Int) { pits(storeA) = 0 pits(storeB) = 0 - def playerUpperHouseValue(index: Int): Int = { - require(index >= 0 && index < storeA) - pits(index + 7) - } - def playerUpperPits: Array[Int] = pits.slice(storeA + 1, storeB) private def playersStoreIndex(playerPosition: PlayerPosition): Int = { @@ -26,10 +21,15 @@ class Board(seedsInPit: Int) { } def playerLowerHouseValue(index: Int): Int = { - require(index >= 0 && index < storeA) + require(index >= 0 && index < storeA, s"Invalid index $index") pits(index) } + def playerUpperHouseValue(index: Int): Int = { + require(index >= 0 && index < storeA, s"Invalid index $index") + pits(index + 7) + } + def playerLowerPits: Array[Int] = pits.slice(0, storeA) def playerPits(playerPosition: PlayerPosition): Array[Int] = { @@ -134,6 +134,7 @@ class Board(seedsInPit: Int) { override def clone(): Board = { val toReturn = new Board(0) + toReturn.toMove = toMove for (i <- toReturn.pits.indices) toReturn.pits(i) = pits(i) toReturn diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/AI/MinMaxAlgorithmTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/AI/MinMaxAlgorithmTest.scala index 8420f76..aa08bcb 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/AI/MinMaxAlgorithmTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/AI/MinMaxAlgorithmTest.scala @@ -1,12 +1,18 @@ package GameObjects.AI +import GameObjects.AI.minimax.{FixedDepth, Leaf, MinMaxAlgorithm, Node, Tree, TreeVisitor} import GameObjects.Utilities.{Board, PlayerUpper} import org.scalatest.BeforeAndAfterEach +import org.scalatest.funsuite.AnyFunSuite + +import scala.language.postfixOps + +class MinMaxAlgorithmTest extends AnyFunSuite with BeforeAndAfterEach { -class MinMaxAlgorithmTest extends org.scalatest.FunSuite with BeforeAndAfterEach { var board: Board = _ var minMaxAlgorithm: MinMaxAlgorithm = _ val depth = 5 + val visitor: TreeVisitor = TreeChildrenCounter() override def beforeEach() { board = new Board(4) @@ -15,40 +21,48 @@ class MinMaxAlgorithmTest extends org.scalatest.FunSuite with BeforeAndAfterEach test("generateCombinations test") { val depth = 3 - val result = minMaxAlgorithm.generateCombinations(depth) - val first = result.head - val third = result(2) - val threeFourTwoIndex = Math.pow(6, depth - 1) * 3 + Math.pow(6, depth - 2) * 4 + 2 - val threeFourTwo = result(threeFourTwoIndex.toInt) - - assert(result.length == Math.pow(6, depth).toInt) - result.foreach(list => { - assert(list.length == depth) - }) + val result = minMaxAlgorithm.generateCombinations(depth).asInstanceOf[Node] + assert(result.getDepth == 3) - assert(first == List(0,0,0)) - assert(third == List(0,0,2)) - assert(threeFourTwo == List(3,4,2)) + result.accept(TreeChildrenCounter()) } test("generateCombinations depth 0 returns empty") { val depth = 0 val result = minMaxAlgorithm.generateCombinations(depth) - assert(result.isEmpty) + assert(result.isInstanceOf[Leaf]) } test("generateCombinations depth 1 returns number of pits") { val depth = 1 - val result = minMaxAlgorithm.generateCombinations(depth) + val result = minMaxAlgorithm.generateCombinations(depth).asInstanceOf[Node] var iterator = 0 - assert(result.length == Math.pow(6, 1).toInt) - result.foreach(list => { - assert(list.length == depth) - assert(list.head == iterator) + assert(result.children.length == Math.pow(6, 1).toInt) + result.children.foreach(child => { + assert(child.isInstanceOf[Leaf]) + assert(child.asInstanceOf[Leaf].houseToMove == iterator) iterator += 1 }) } - + case class TreeChildrenCounter() extends TreeVisitor { + override def visit(tree: Tree): Unit = { + tree match { + case asNode: Node => + assert(asNode.children.length == 6) + asNode.children.foldLeft(0)((acc, node) => { + node match { + case leaf: Leaf => + assert(leaf.houseToMove == acc) + case node: Node => + assert(node.houseToMove == acc) + } + acc + 1 + }) + asNode.children.foreach(tree => tree.accept(this)) + case _: Leaf => + } + } + } } diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala new file mode 100644 index 0000000..3aae369 --- /dev/null +++ b/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala @@ -0,0 +1,47 @@ +package GameObjects.AI.minimax + +import GameObjects.AI.evaluation.EvaluationByResult +import GameObjects.Outputs.ConsoleOutput +import GameObjects.Utilities.{Board, PlayerLower, PlayerUpper} +import org.scalatest.funsuite.AnyFunSuite + +class MiniMaxVisitorTest extends AnyFunSuite { + + val miniMaxVisitor: MiniMaxVisitor = new MiniMaxVisitor(new EvaluationByResult) + val miniMaxAlgorithm = new MinMaxAlgorithm(new Board(1), PlayerLower(), new FixedDepth(3)) + + test("should take test") { + val board = new Board(0) + board.toMove = PlayerLower() + val pits = board.pits + for (i <- 0 to 2) + pits(i * 2) = 1 + for (i <- 0 to 1) + pits(i * 2 + 9) = 100 / (i * 10 + 1) + + + val tree = miniMaxAlgorithm.generateCombinations(5) + val resultForWinner = miniMaxVisitor.minimax(tree, PlayerLower(), board) + new ConsoleOutput(board).printGame() + board.toMove = PlayerUpper() + val resultForLoser = miniMaxVisitor.minimax(tree, PlayerUpper(), board) + + assert(resultForWinner == 2) + assert(resultForLoser == 2) + } + + test("redo move test") { + val board = new Board(0) + board.toMove = PlayerLower() + val pits = board.pits + pits(5) = 1 + pits(4) = 2 + pits(3) = 4 + new ConsoleOutput(board).printGame() + + val tree = miniMaxAlgorithm.generateCombinations(9) + val result = miniMaxVisitor.minimax(tree, PlayerLower(), board) + + assert(result == 5) + } +} From c6607555c53116c399e59c34d7a19ae6eb8ca948 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Sun, 9 May 2021 23:58:52 +0200 Subject: [PATCH 18/24] set ai depth by user --- .../src/main/scala/Actors/GameManager.scala | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala b/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala index 0e06cb2..a9f087f 100644 --- a/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala +++ b/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala @@ -1,28 +1,37 @@ package Actors -import GameObjects.AI.{AiAlgorithm, HumanPlayer} +import GameObjects.AI.minimax.{FixedDepth, MinMaxAlgorithm} +import GameObjects.AI.{HumanPlayer, MoveDecider} import GameObjects.Outputs.ConsoleOutput -import GameObjects.Utilities.{Board, GameFinished, PlayerLower, PlayerUpper} +import GameObjects.Utilities._ import akka.actor.{Actor, Props} +import scala.io.StdIn + class GameManager extends Actor{ setUp() private def setUp(): Unit = { - val board = new Board(1) + val board = new Board(4) println("playerA: (h/ai) or end to finish") val playerAIn = scala.io.StdIn.readLine() if (playerAIn == "end") context.system.terminate() else { - val playerA = if (playerAIn == "h") new HumanPlayer(board) else new AiAlgorithm(board, PlayerUpper(), 2) + val playerA = if (playerAIn == "h") new HumanPlayer(board) else getAi(PlayerUpper(), board) println("playerB: (h/ai)") val playerBIn = scala.io.StdIn.readLine() - val playerB = if (playerBIn == "h") new HumanPlayer(board) else new AiAlgorithm(board, PlayerLower(), 1) + val playerB = if (playerBIn == "h") new HumanPlayer(board) else getAi(PlayerLower(), board) val output = new ConsoleOutput(board) output.printGame() context.actorOf(Props(classOf[Server], playerA, playerB, board, Long.MaxValue: Long, output)) } } + def getAi(position: PlayerPosition, board: Board): MoveDecider = { + println("Ai depth:") + val depth = StdIn.readInt() + new MinMaxAlgorithm(board, position, new FixedDepth(depth)) + } + override def receive: Receive = { case GameFinished(string) => println("Game finished " + string) From f2bf912b651f703fefa08cca8617fce8d3a7ec2b Mon Sep 17 00:00:00 2001 From: mprzymus Date: Sun, 9 May 2021 23:59:21 +0200 Subject: [PATCH 19/24] renamed --- lista10-kalahaGame/build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lista10-kalahaGame/build.sbt b/lista10-kalahaGame/build.sbt index 3b378f0..fb9c395 100644 --- a/lista10-kalahaGame/build.sbt +++ b/lista10-kalahaGame/build.sbt @@ -1,4 +1,4 @@ -name := "lista10-kalahaGame" +name := "kalahaGame" version := "0.1" From 55df56f0a81f4a347a569da81a4b92ee64032c6f Mon Sep 17 00:00:00 2001 From: mprzymus Date: Mon, 10 May 2021 00:03:17 +0200 Subject: [PATCH 20/24] end of game fixed --- .../scala/GameObjects/Utilities/Board.scala | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala index 8c06368..0e47cb8 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala @@ -4,7 +4,7 @@ class Board(seedsInPit: Int) { val pits: Array[Int] = Array.fill(14) { seedsInPit } - var toMove : PlayerPosition = PlayerUpper() + var toMove: PlayerPosition = PlayerUpper() private val storeA = 6 private val storeB = 13 pits(storeA) = 0 @@ -40,7 +40,7 @@ class Board(seedsInPit: Int) { } } - def playerPit(player : PlayerPosition, index : Int): Int = { + def playerPit(player: PlayerPosition, index: Int): Int = { player match { case PlayerUpper() => playerUpperHouseValue(index) case PlayerLower() => playerLowerHouseValue(index) @@ -65,7 +65,8 @@ class Board(seedsInPit: Int) { val realIndex = findRealIndex(pit, playerPosition) val opponentsStore = opponentsStoreIndex(realIndex) val seeds = pits(realIndex) - if (pits(realIndex) == 0) throw new IllegalArgumentException("Cannot move from empty pit number " + pit + " realIndex = " + realIndex) + if (pits(realIndex) == 0) + throw new IllegalArgumentException("Cannot move from empty pit number " + pit + " realIndex = " + realIndex) pits(realIndex) = 0 @scala.annotation.tailrec @@ -76,24 +77,32 @@ class Board(seedsInPit: Int) { pitVisitor(index, seeds - 1) } else if (!isHouse(previousIndex)) { - if (sumPoints(playerPosition) == 0) finishGame(playerPosition) + if (endOfGame()) finishGame(playerPosition) else playerPosition } else if (samePlayers(realIndex, previousIndex) && pits(previousIndex) == 1) { + toMove = toMove.opponent val opposite = oppositeIndex(previousIndex) val store = playersStoreIndex(previousIndex) pits(store) += pits(opposite) pits(opposite) = 0 pits(previousIndex) = 0 pits(store) += 1 - if (sumPoints(playerPosition) == 0) finishGame(playerPosition) + if (endOfGame()) finishGame(playerPosition) + else playerPosition.opponent + } + else { + toMove = toMove.opponent + if (endOfGame()) finishGame(playerPosition) else playerPosition.opponent } - else playerPosition.opponent } - toMove = pitVisitor(realIndex, seeds) - toMove + pitVisitor(realIndex, seeds) + } + + private def endOfGame() = { + sumPoints(toMove) == 0 } private def findRealIndex(index: Int, player: PlayerPosition) = { @@ -142,7 +151,7 @@ class Board(seedsInPit: Int) { } object Board { - def createPosition(allPits: Seq[Int], nextMoves: PlayerPosition) : Board ={ + def createPosition(allPits: Seq[Int], nextMoves: PlayerPosition): Board = { require(allPits.length == 14, "invalid array length " + allPits.length) val board = new Board(1) for (i <- board.pits.indices) From 9983a54551db6a5c8ffbc98c9a5d107749cbe3f2 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Mon, 10 May 2021 16:57:27 +0200 Subject: [PATCH 21/24] replaced tree iteration with recursion --- .../AI/minimax/MinMaxAlgorithm.scala | 23 +------- .../AI/minimax/MiniMaxVisitor.scala | 58 ++++++++++--------- .../GameObjects/AI/MinMaxAlgorithmTest.scala | 48 --------------- .../AI/minimax/MiniMaxVisitorTest.scala | 8 +-- 4 files changed, 37 insertions(+), 100 deletions(-) diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MinMaxAlgorithm.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MinMaxAlgorithm.scala index cc75737..a2274d8 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MinMaxAlgorithm.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MinMaxAlgorithm.scala @@ -8,27 +8,10 @@ class MinMaxAlgorithm(private val gameBoard: Board, private val aisPosition: Pla private val depthDetermination: DepthDetermination, private val aiAlgorithm: MiniMaxVisitor = new MiniMaxVisitor(new EvaluationByResult)) extends MoveDecider { - def generateCombinations(determineDepth: Int): Tree = { - def createList(depthToDo: Int, nodeValue: Int): Tree = { - if (depthToDo == 0) { - Leaf(nodeValue) - } - else { - val children = (0 to 5) - .map(x => createList(depthToDo - 1, x)) - .toList - if (nodeValue >= 0) - Node(nodeValue, children) - else Root(children) - } - } - - createList(determineDepth, -1) - } - override def getMove: Int = { - val combinations = generateCombinations(depthDetermination.determineDepth) - aiAlgorithm.minimax(combinations, aisPosition, gameBoard) + val move = aiAlgorithm.minimax(depthDetermination.determineDepth, aisPosition, gameBoard) + println(s"Ai choose $move") + move } override def badMoveInform(message: String): Unit = println("ai missed: " + message) diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MiniMaxVisitor.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MiniMaxVisitor.scala index 7452d47..9ca49f0 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MiniMaxVisitor.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MiniMaxVisitor.scala @@ -3,66 +3,65 @@ package GameObjects.AI.minimax import GameObjects.AI.evaluation.EvaluationStrategy import GameObjects.Utilities.{Board, GameFinished, PlayerPosition} -class MiniMaxVisitor(evaluationStrategy: EvaluationStrategy) extends TreeVisitor { +class MiniMaxVisitor(evaluationStrategy: EvaluationStrategy) { private var playerPosition: PlayerPosition = _ - override def visit(tree: Tree): Unit = ??? - def isLegalToMove(houseToMove: Int, board: Board, player: PlayerPosition): Boolean = { board.playerPit(player, houseToMove) != 0 } - def minimax(tree: Tree, player: PlayerPosition, board: Board): Int = { - tree match { - case asLeaf: Leaf => - move(player, board, asLeaf) - case asNode: Node => - if (isLegalToMove(asNode.houseToMove, board, board.toMove)) { + private def minimax(depth: Int, player: PlayerPosition, board: Board, houseToMove: Int): Int = { + depth match { + case x if x < 0 => throw new IllegalArgumentException(s"depth = $x") + case 0 => + move(player, board, houseToMove) + case _ => + if (isLegalToMove(houseToMove, board, board.toMove)) { if (!shouldSkipTurn(player, board)) { - move(player, board, asNode) + moveNode(player, board, houseToMove, depth - 1) } else { - moveAllPossibilities(asNode, board.toMove, board) + moveAllPossibilities(board.toMove, board, depth - 1) } } else { Int.MinValue } - - case asRoot: Root => - playerPosition = player - val children = asRoot.children.map(child => minimax(child, player, board.clone())) - children.indices.maxBy(children) } } + def minimax(depth: Int, player: PlayerPosition, board: Board): Int = { + playerPosition = player + val children = iterateOverPossibilities(depth, player, board) + children.indices.maxBy(children) + } + + private def iterateOverPossibilities(depth: Int, player: PlayerPosition, board: Board) = { + (0 to 5).map(child => minimax(depth, player, board.clone(), child)) + } + private def shouldSkipTurn(player: PlayerPosition, board: Board) = { player != board.toMove } - private def move(player: PlayerPosition, board: Board, asNode: Node) = { - val nextPlayer = board.move(asNode.houseToMove, board.toMove) + private def moveNode(player: PlayerPosition, board: Board, house: Int, nodeDepth: Int) = { + val nextPlayer = board.move(house, board.toMove) if (nextPlayer == GameFinished()) { handleGameFinished(evaluationStrategy.evaluate(player, board)) } else { val nextPlayer = player.opponent - moveAllPossibilities(asNode, nextPlayer, board) + moveAllPossibilities(nextPlayer, board, nodeDepth) } } - private def moveAllPossibilities(asNode: Node, nextPlayer: PlayerPosition, board: Board) = { - val children = asNode.children.map(child => minimax(child, nextPlayer, board.clone())) - children.max * -1 // always max cause evaluation function returns value for current player - } - - private def move(player: PlayerPosition, board: Board, house: Leaf) = { + private def move(player: PlayerPosition, board: Board, house: Int) = { if (board.toMove != player) { val result = evaluationStrategy.evaluate(player, board) result } - else if (isLegalToMove(house.houseToMove, board, player)) { - val next = board.move(house.houseToMove, board.toMove) + else if (isLegalToMove(house, board, player)) { + val next = board.move(house, board.toMove) val result = evaluationStrategy.evaluate(player, board) if (next == GameFinished()) { handleGameFinished(result) @@ -77,6 +76,11 @@ class MiniMaxVisitor(evaluationStrategy: EvaluationStrategy) extends TreeVisitor } } + private def moveAllPossibilities(nextPlayer: PlayerPosition, board: Board, depth: Int) = { + val children = iterateOverPossibilities(depth, nextPlayer, board) + children.max * -1 // always max cause evaluation function returns value for current player + } + private def handleGameFinished(result: Int) = { result } diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/AI/MinMaxAlgorithmTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/AI/MinMaxAlgorithmTest.scala index aa08bcb..38e3c92 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/AI/MinMaxAlgorithmTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/AI/MinMaxAlgorithmTest.scala @@ -12,57 +12,9 @@ class MinMaxAlgorithmTest extends AnyFunSuite with BeforeAndAfterEach { var board: Board = _ var minMaxAlgorithm: MinMaxAlgorithm = _ val depth = 5 - val visitor: TreeVisitor = TreeChildrenCounter() override def beforeEach() { board = new Board(4) minMaxAlgorithm = new MinMaxAlgorithm(board, PlayerUpper(), new FixedDepth(depth)) } - - test("generateCombinations test") { - val depth = 3 - val result = minMaxAlgorithm.generateCombinations(depth).asInstanceOf[Node] - assert(result.getDepth == 3) - - result.accept(TreeChildrenCounter()) - } - - test("generateCombinations depth 0 returns empty") { - val depth = 0 - val result = minMaxAlgorithm.generateCombinations(depth) - - assert(result.isInstanceOf[Leaf]) - } - - test("generateCombinations depth 1 returns number of pits") { - val depth = 1 - val result = minMaxAlgorithm.generateCombinations(depth).asInstanceOf[Node] - var iterator = 0 - - assert(result.children.length == Math.pow(6, 1).toInt) - result.children.foreach(child => { - assert(child.isInstanceOf[Leaf]) - assert(child.asInstanceOf[Leaf].houseToMove == iterator) - iterator += 1 - }) - } - case class TreeChildrenCounter() extends TreeVisitor { - override def visit(tree: Tree): Unit = { - tree match { - case asNode: Node => - assert(asNode.children.length == 6) - asNode.children.foldLeft(0)((acc, node) => { - node match { - case leaf: Leaf => - assert(leaf.houseToMove == acc) - case node: Node => - assert(node.houseToMove == acc) - } - acc + 1 - }) - asNode.children.foreach(tree => tree.accept(this)) - case _: Leaf => - } - } - } } diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala index 3aae369..77f542d 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala @@ -20,11 +20,10 @@ class MiniMaxVisitorTest extends AnyFunSuite { pits(i * 2 + 9) = 100 / (i * 10 + 1) - val tree = miniMaxAlgorithm.generateCombinations(5) - val resultForWinner = miniMaxVisitor.minimax(tree, PlayerLower(), board) + val resultForWinner = miniMaxVisitor.minimax(5, PlayerLower(), board) new ConsoleOutput(board).printGame() board.toMove = PlayerUpper() - val resultForLoser = miniMaxVisitor.minimax(tree, PlayerUpper(), board) + val resultForLoser = miniMaxVisitor.minimax(5, PlayerUpper(), board) assert(resultForWinner == 2) assert(resultForLoser == 2) @@ -39,8 +38,7 @@ class MiniMaxVisitorTest extends AnyFunSuite { pits(3) = 4 new ConsoleOutput(board).printGame() - val tree = miniMaxAlgorithm.generateCombinations(9) - val result = miniMaxVisitor.minimax(tree, PlayerLower(), board) + val result = miniMaxVisitor.minimax(10, PlayerLower(), board) assert(result == 5) } From 85232b9d57fe2ce0ca7c21636bc3cbcce7b61e8e Mon Sep 17 00:00:00 2001 From: mprzymus Date: Mon, 10 May 2021 17:48:17 +0200 Subject: [PATCH 22/24] refactoring --- .../src/main/scala/Actors/GameManager.scala | 3 +- .../src/main/scala/Actors/Server.scala | 30 +++++++++++-------- .../GameObjects/Outputs/ConsoleOutput.scala | 2 +- .../GameObjects/AI/AiAlgorithmTest.scala | 2 -- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala b/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala index a9f087f..1faed67 100644 --- a/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala +++ b/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala @@ -33,8 +33,7 @@ class GameManager extends Actor{ } override def receive: Receive = { - case GameFinished(string) => - println("Game finished " + string) + case GameFinished(_) => setUp() } } diff --git a/lista10-kalahaGame/src/main/scala/Actors/Server.scala b/lista10-kalahaGame/src/main/scala/Actors/Server.scala index 8ee00d3..1a8ed1a 100644 --- a/lista10-kalahaGame/src/main/scala/Actors/Server.scala +++ b/lista10-kalahaGame/src/main/scala/Actors/Server.scala @@ -18,19 +18,17 @@ class Server(playerA: MoveDecider, playerB: MoveDecider, private val board: Boar case turn: Move => try { if (timer.getTimeSeconds < timeForTurn) { - if (sender() == upperChild) { - val destination = board.move(turn.house, PlayerUpper()) - if (destination.isInstanceOf[GameFinished]) - context.parent ! GameFinished("player A won") - else findDestination(destination) ! MakeMove() - } else if (sender() == lowerChild) { - val destination = board.move(turn.house, PlayerLower()) - if (destination.isInstanceOf[GameFinished]) - context.parent ! GameFinished("player B won") - else findDestination(destination) ! MakeMove() + val player = + if (sender() == lowerChild) PlayerLower() + else if (sender() == upperChild) PlayerUpper() + else throw new InvalidSenderException("Unknown sender") + val destination = board.move(turn.house, player) + if (destination.isInstanceOf[GameFinished]) + finishGame() + else { + serverOutput.printGame() + findDestination(destination) ! MakeMove() } - else throw new InvalidSenderException("Unknown sender") - serverOutput.printGame() timer.restart() } else context.parent ! GameFinished("time out") @@ -42,7 +40,13 @@ class Server(playerA: MoveDecider, playerB: MoveDecider, private val board: Boar sender() ! BadMove() } case GameFinished(_) => - context.parent ! GameFinished() + finishGame() + } + + private def finishGame() = { + val message = s"Game finished. Result: ${board.playerUpperScore} - ${board.playerLowerScore}" + serverOutput.putMessage(message) + context.parent ! GameFinished() } private def findDestination(sendTo: PlayerPosition): ActorRef = { diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Outputs/ConsoleOutput.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Outputs/ConsoleOutput.scala index 35ac197..122c5c8 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/Outputs/ConsoleOutput.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Outputs/ConsoleOutput.scala @@ -35,5 +35,5 @@ class ConsoleOutput(private val board: Board) extends Output { println("to move " + board.toMove) } - override def putMessage(message: String): Unit = println("message") + override def putMessage(message: String): Unit = println(message) } diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/AI/AiAlgorithmTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/AI/AiAlgorithmTest.scala index 0681809..b617d1c 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/AI/AiAlgorithmTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/AI/AiAlgorithmTest.scala @@ -1,6 +1,5 @@ package GameObjects.AI -import GameObjects.AI.AiAlgorithm import GameObjects.Outputs.ConsoleOutput import GameObjects.Utilities.{Board, PlayerLower, PlayerUpper} @@ -67,5 +66,4 @@ class AiAlgorithmTest extends org.scalatest.FunSuite { val ai = new AiAlgorithm(board, player, 1) assert(ai.getMove == 3) } - } From bb2e63ea6ba5a9f85d1162f95b111f6b41efdb07 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Mon, 17 May 2021 17:19:15 +0200 Subject: [PATCH 23/24] refactoring --- lista10-kalahaGame/build.sbt | 4 ++- .../src/main/scala/Actors/GameManager.scala | 11 ++++++ .../src/main/scala/Actors/Server.scala | 5 +-- .../evaluation/FakeEvaluationStrategy.scala | 10 ++++++ .../AI/minimax/MiniMaxVisitor.scala | 29 ++++++++------- .../scala/GameObjects/Utilities/Board.scala | 35 ++++++++++++------- .../GameObjects/AI/AiAlgorithmTest.scala | 3 +- .../AI/minimax/MiniMaxVisitorTest.scala | 10 ++++-- .../Outputs/ConsoleOutputTest.scala | 3 +- .../GameObjects/Utilities/BoardTest.scala | 12 ++----- 10 files changed, 78 insertions(+), 44 deletions(-) create mode 100644 lista10-kalahaGame/src/main/scala/GameObjects/AI/evaluation/FakeEvaluationStrategy.scala diff --git a/lista10-kalahaGame/build.sbt b/lista10-kalahaGame/build.sbt index fb9c395..53f72ea 100644 --- a/lista10-kalahaGame/build.sbt +++ b/lista10-kalahaGame/build.sbt @@ -5,4 +5,6 @@ version := "0.1" scalaVersion := "2.13.1" libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.6.1" -libraryDependencies += "org.scalatest" % "scalatest_2.13" % "3.1.0" % "test" +libraryDependencies += "org.scalatest" % "scalatest_2.13" % "3.2.7" % "test" +libraryDependencies += "org.scalatestplus" %% "mockito-3-4" % "3.2.7.0" % "test" + diff --git a/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala b/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala index 1faed67..2e0ffc2 100644 --- a/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala +++ b/lista10-kalahaGame/src/main/scala/Actors/GameManager.scala @@ -7,6 +7,8 @@ import GameObjects.Utilities._ import akka.actor.{Actor, Props} import scala.io.StdIn +import scala.io.StdIn.readLine +import scala.util.Random class GameManager extends Actor{ setUp() @@ -20,12 +22,21 @@ class GameManager extends Actor{ println("playerB: (h/ai)") val playerBIn = scala.io.StdIn.readLine() val playerB = if (playerBIn == "h") new HumanPlayer(board) else getAi(PlayerLower(), board) + firstRandomMove(board) val output = new ConsoleOutput(board) output.printGame() context.actorOf(Props(classOf[Server], playerA, playerB, board, Long.MaxValue: Long, output)) } } + private def firstRandomMove(board: Board) = { + println("First random? (y/n)") + val firstRandom = readLine() + if (firstRandom == "y") { + board.move(Random.nextInt(6), PlayerUpper()) + } + } + def getAi(position: PlayerPosition, board: Board): MoveDecider = { println("Ai depth:") val depth = StdIn.readInt() diff --git a/lista10-kalahaGame/src/main/scala/Actors/Server.scala b/lista10-kalahaGame/src/main/scala/Actors/Server.scala index 1a8ed1a..173dbe0 100644 --- a/lista10-kalahaGame/src/main/scala/Actors/Server.scala +++ b/lista10-kalahaGame/src/main/scala/Actors/Server.scala @@ -12,7 +12,8 @@ class Server(playerA: MoveDecider, playerB: MoveDecider, private val board: Boar val timer = new Timer() val upperChild: ActorRef = context.actorOf(Props(classOf[Player], playerA)) val lowerChild: ActorRef = context.actorOf(Props(classOf[Player], playerB)) - upperChild ! MakeMove() + + (if (board.toMove == PlayerUpper()) upperChild else lowerChild) ! MakeMove() override def receive: Receive = { case turn: Move => @@ -43,7 +44,7 @@ class Server(playerA: MoveDecider, playerB: MoveDecider, private val board: Boar finishGame() } - private def finishGame() = { + private def finishGame(): Unit = { val message = s"Game finished. Result: ${board.playerUpperScore} - ${board.playerLowerScore}" serverOutput.putMessage(message) context.parent ! GameFinished() diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/evaluation/FakeEvaluationStrategy.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/evaluation/FakeEvaluationStrategy.scala new file mode 100644 index 0000000..14aeba6 --- /dev/null +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/evaluation/FakeEvaluationStrategy.scala @@ -0,0 +1,10 @@ +package GameObjects.AI.evaluation +import GameObjects.Utilities.{Board, PlayerPosition} + +class FakeEvaluationStrategy extends EvaluationStrategy { + var x = 0 + override def evaluate(player: PlayerPosition, board: Board): Int = { + x += 1 + x + } +} diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MiniMaxVisitor.scala b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MiniMaxVisitor.scala index 9ca49f0..c68a873 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MiniMaxVisitor.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/AI/minimax/MiniMaxVisitor.scala @@ -3,18 +3,18 @@ package GameObjects.AI.minimax import GameObjects.AI.evaluation.EvaluationStrategy import GameObjects.Utilities.{Board, GameFinished, PlayerPosition} -class MiniMaxVisitor(evaluationStrategy: EvaluationStrategy) { - private var playerPosition: PlayerPosition = _ +class MiniMaxVisitor(protected val evaluationStrategy: EvaluationStrategy, protected val nodeChildrenNumber: Int = 5) { + private var maxPlayerPosition: PlayerPosition = _ def isLegalToMove(houseToMove: Int, board: Board, player: PlayerPosition): Boolean = { board.playerPit(player, houseToMove) != 0 } - private def minimax(depth: Int, player: PlayerPosition, board: Board, houseToMove: Int): Int = { + protected def minimax(depth: Int, player: PlayerPosition, board: Board, houseToMove: Int): Int = { depth match { case x if x < 0 => throw new IllegalArgumentException(s"depth = $x") case 0 => - move(player, board, houseToMove) + countPosition(player, board, houseToMove) case _ => if (isLegalToMove(houseToMove, board, board.toMove)) { if (!shouldSkipTurn(player, board)) { @@ -31,13 +31,13 @@ class MiniMaxVisitor(evaluationStrategy: EvaluationStrategy) { } def minimax(depth: Int, player: PlayerPosition, board: Board): Int = { - playerPosition = player + maxPlayerPosition = player val children = iterateOverPossibilities(depth, player, board) children.indices.maxBy(children) } - private def iterateOverPossibilities(depth: Int, player: PlayerPosition, board: Board) = { - (0 to 5).map(child => minimax(depth, player, board.clone(), child)) + protected def iterateOverPossibilities(depth: Int, player: PlayerPosition, board: Board): Seq[Int] = { + (0 to nodeChildrenNumber).map(child => minimax(depth, player, board.clone(), child)) } private def shouldSkipTurn(player: PlayerPosition, board: Board) = { @@ -55,25 +55,24 @@ class MiniMaxVisitor(evaluationStrategy: EvaluationStrategy) { } } - private def move(player: PlayerPosition, board: Board, house: Int) = { - if (board.toMove != player) { + private def countPosition(player: PlayerPosition, board: Board, house: Int) = { + var toReturn: Int = Int.MinValue; + if (shouldSkipTurn(player, board)) { val result = evaluationStrategy.evaluate(player, board) - result + toReturn = result } else if (isLegalToMove(house, board, player)) { val next = board.move(house, board.toMove) val result = evaluationStrategy.evaluate(player, board) if (next == GameFinished()) { - handleGameFinished(result) + toReturn = handleGameFinished(result) } else { val result = evaluationStrategy.evaluate(player, board) - result + toReturn = result } } - else { - Int.MinValue - } + toReturn } private def moveAllPossibilities(nextPlayer: PlayerPosition, board: Board, depth: Int) = { diff --git a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala index 0e47cb8..8bc6a33 100644 --- a/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala +++ b/lista10-kalahaGame/src/main/scala/GameObjects/Utilities/Board.scala @@ -77,23 +77,22 @@ class Board(seedsInPit: Int) { pitVisitor(index, seeds - 1) } else if (!isHouse(previousIndex)) { - if (endOfGame()) finishGame(playerPosition) + if (endOfGame()) finishGame else playerPosition } - else if (samePlayers(realIndex, previousIndex) && pits(previousIndex) == 1) { + else if (samePlayers(realIndex, previousIndex) && pits(previousIndex) == 1) { //may take toMove = toMove.opponent val opposite = oppositeIndex(previousIndex) val store = playersStoreIndex(previousIndex) - pits(store) += pits(opposite) - pits(opposite) = 0 - pits(previousIndex) = 0 - pits(store) += 1 - if (endOfGame()) finishGame(playerPosition) + if (pits(opposite) != 0) { + take(previousIndex, opposite, store) + } + if (endOfGame()) finishGame else playerPosition.opponent } else { toMove = toMove.opponent - if (endOfGame()) finishGame(playerPosition) + if (endOfGame()) finishGame else playerPosition.opponent } } @@ -101,6 +100,13 @@ class Board(seedsInPit: Int) { pitVisitor(realIndex, seeds) } + private def take(previousIndex: Int, opposite: Int, store: Int): Unit = { + pits(store) += pits(opposite) + pits(opposite) = 0 + pits(previousIndex) = 0 + pits(store) += 1 + } + private def endOfGame() = { sumPoints(toMove) == 0 } @@ -133,11 +139,14 @@ class Board(seedsInPit: Int) { private def sumPoints(player: PlayerPosition) = playerPits(player).sum - private def finishGame(winner: PlayerPosition): PlayerPosition = { - pits(playersStoreIndex(winner)) += sumPoints(winner.opponent) - val losersStore = playersStoreIndex(winner.opponent) - for (i <- losersStore - storeA until losersStore) - pits(i) = 0 + private def finishGame: PlayerPosition = { + pits(playersStoreIndex(toMove.opponent)) += sumPoints(toMove) + sumPoints(toMove.opponent) + for (i <- 0 to storeB) { + if (!(i == storeA || i == storeB)) { + pits(i) = 0 + } + } + toMove = GameFinished() GameFinished() } diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/AI/AiAlgorithmTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/AI/AiAlgorithmTest.scala index b617d1c..9d92cb0 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/AI/AiAlgorithmTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/AI/AiAlgorithmTest.scala @@ -2,8 +2,9 @@ package GameObjects.AI import GameObjects.Outputs.ConsoleOutput import GameObjects.Utilities.{Board, PlayerLower, PlayerUpper} +import org.scalatest.funsuite.AnyFunSuite -class AiAlgorithmTest extends org.scalatest.FunSuite { +class AiAlgorithmTest extends AnyFunSuite { test("win in one move test") { val board = new Board(1) val player = PlayerLower() diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala index 77f542d..ef89004 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala @@ -1,13 +1,13 @@ package GameObjects.AI.minimax -import GameObjects.AI.evaluation.EvaluationByResult +import GameObjects.AI.evaluation.{EvaluationByResult, FakeEvaluationStrategy} import GameObjects.Outputs.ConsoleOutput import GameObjects.Utilities.{Board, PlayerLower, PlayerUpper} import org.scalatest.funsuite.AnyFunSuite class MiniMaxVisitorTest extends AnyFunSuite { - val miniMaxVisitor: MiniMaxVisitor = new MiniMaxVisitor(new EvaluationByResult) + var miniMaxVisitor: MiniMaxVisitor = new MiniMaxVisitor(new EvaluationByResult) val miniMaxAlgorithm = new MinMaxAlgorithm(new Board(1), PlayerLower(), new FixedDepth(3)) test("should take test") { @@ -42,4 +42,10 @@ class MiniMaxVisitorTest extends AnyFunSuite { assert(result == 5) } + + test("Node check test") { + miniMaxVisitor = new MiniMaxVisitor(new FakeEvaluationStrategy(), 2) + //val board = mock(classOf[Board]) + //val result = miniMaxVisitor.minimax(2, PlayerLower()) + } } diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/Outputs/ConsoleOutputTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/Outputs/ConsoleOutputTest.scala index 987d571..275c4ea 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/Outputs/ConsoleOutputTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/Outputs/ConsoleOutputTest.scala @@ -1,9 +1,10 @@ package GameObjects.Outputs import GameObjects.Utilities.Board +import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should._ -class ConsoleOutputTest extends org.scalatest.FunSuite with Matchers{ +class ConsoleOutputTest extends AnyFunSuite with Matchers{ val printer = new ConsoleOutput(new Board(6)) test("printGame no throws test") { noException should be thrownBy printer.printGame() diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala index f883dd4..ea66d7e 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/Utilities/BoardTest.scala @@ -2,8 +2,9 @@ package GameObjects.Utilities import GameObjects.Outputs.ConsoleOutput import org.scalatest._ +import org.scalatest.funsuite.AnyFunSuite -class BoardTest extends FunSuite { +class BoardTest extends AnyFunSuite { val seedsInPit = 6 val board = new Board(seedsInPit) @@ -78,13 +79,6 @@ class BoardTest extends FunSuite { assert(board.playerLowerScore == 12) } - test("game finished last seed in player's store test") { - val board = makeGameToFinishInOneTurn() - assert(GameFinished() == board.move(5, PlayerLower())) - assert(36 == board.playerLowerScore) - assert(0 == board.playerUpperScore) - } - def makeGameToFinishInOneTurn(): Board = { val player = PlayerLower() val position = Array(0, 0, 0, 0, 0, 8, 7, 5, 4, 1, 4, 4, 3, 0) @@ -127,7 +121,7 @@ class BoardTest extends FunSuite { } } -class BoardTakingTest extends FunSuite with BeforeAndAfterEach { +class BoardTakingTest extends AnyFunSuite with BeforeAndAfterEach { val seedsInPit = 4 var board : Board = _ From a66c2bcd1ef895e6ad1494bf4bd2976d9f0dd6d6 Mon Sep 17 00:00:00 2001 From: mprzymus Date: Mon, 17 May 2021 18:30:59 +0200 Subject: [PATCH 24/24] test fixed to fit updated in previous commit game rules --- .../test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala b/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala index ef89004..dfb3222 100644 --- a/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala +++ b/lista10-kalahaGame/src/test/scala/GameObjects/AI/minimax/MiniMaxVisitorTest.scala @@ -36,9 +36,10 @@ class MiniMaxVisitorTest extends AnyFunSuite { pits(5) = 1 pits(4) = 2 pits(3) = 4 + pits(10) = 1000 new ConsoleOutput(board).printGame() - val result = miniMaxVisitor.minimax(10, PlayerLower(), board) + val result = miniMaxVisitor.minimax(6, PlayerLower(), board) assert(result == 5) }