|      1 // Resit Part about an Evil Wordle Clone |      1 // Main Part 3 about Evil Wordle | 
|      2 //========================================== |      2 //=============================== | 
|      3 // |         | 
|      4 // Task description are on KEATS |         | 
|      5 // |         | 
|      6 // Make sure you use Scala Version 2.13 (not Scala 3) |         | 
|      7 // |         | 
|      8 // Upload your submision to OneSpace and give Senir and Christian |         | 
|      9 // write access to the folder. The folder should have you email |         | 
|     10 // address as name. Give us write permission by July 25th such |         | 
|     11 // that we can download your solution by the closing date August 4th. |         | 
|     12  |      3  | 
|     13 object Resit { |      4 // test bash | 
|         |      5  | 
|         |      6 object M2 {  | 
|     14  |      7  | 
|     15 import io.Source |      8 import io.Source | 
|     16 import scala.util._ |      9 import scala.util._ | 
|     17  |     10  | 
|     18 //============================= |     11 // ADD YOUR CODE BELOW | 
|     19 // Task 1 [0.25 Marks] |     12 //====================== | 
|     20  |     13  | 
|         |     14 // test import | 
|         |     15  | 
|         |     16  | 
|         |     17 //(1) | 
|     21 def get_wordle_list(url: String) : List[String] = { |     18 def get_wordle_list(url: String) : List[String] = { | 
|     22     try{ |     19   Try(Source.fromURL(url)("ISO-8859-1").getLines().toList).getOrElse(Nil) | 
|     23 		val raw = Source.fromURL(url).mkString |         | 
|     24 		raw.split("\n").toList |         | 
|     25 	} |         | 
|     26 	catch { |         | 
|     27 		case e : Exception => List[String]() |         | 
|     28     } |         | 
|     29 } |     20 } | 
|     30  |     21  | 
|     31  |     22  | 
|     32 // val secrets = get_wordle_list("https://nms.kcl.ac.uk/christian.urban/wordle.txt") |     23 // val secrets = get_wordle_list("https://nms.kcl.ac.uk/christian.urban/wordle.txt") | 
|     33 // secrets.length => 12972 |     24 // secrets.length // => 12972 | 
|     34 // secrets.filter(_.length != 5) => Nil |     25 // secrets.filter(_.length != 5) // => Nil | 
|     35  |     26  | 
|     36 //============================= |     27 //(2) | 
|     37 // Task 2 [0.25 Marks] |     28 def removeN[A](xs: List[A], elem: A, n: Int) : List[A] = { | 
|     38  |     29     if (n == 0) xs  | 
|     39 def removeOne[A](xs: List[A], elem: A) : List[A] = { |     30     else xs match { | 
|     40     val index = xs.indexOf(elem) |     31         case Nil => Nil | 
|     41 	val first = xs.take(index) |     32         case x::xs =>  | 
|     42 	val second = xs.drop(index + 1) |     33             if (x == elem) removeN(xs, x, n - 1)  | 
|     43 	first ::: second |     34             else x::removeN(xs, elem, n) | 
|     44 } |     35     }  | 
|     45  |     36 }     | 
|     46 // removeOne(List(1,2,3,2,1), 3)  => List(1, 2, 2, 1) |         | 
|     47 // removeOne(List(1,2,3,2,1), 2)  => List(1, 3, 2, 1) |         | 
|     48 // removeOne(List(1,2,3,2,1), 1)  => List(2, 3, 2, 1) |         | 
|     49 // removeOne(List(1,2,3,2,1), 0)  => List(1, 2, 3, 2, 1) |         | 
|     50  |     37  | 
|     51  |     38  | 
|     52 //============================= |     39 // removeN(List(1,2,3,2,1), 3, 1)  // => List(1, 2, 2, 1) | 
|     53 // Task 3 [1.5 Marks] |     40 // removeN(List(1,2,3,2,1), 2, 1)  // => List(1, 3, 2, 1) | 
|         |     41 // removeN(List(1,2,3,2,1), 1, 1)  // => List(2, 3, 2, 1) | 
|         |     42 // removeN(List(1,2,3,2,1), 0, 2)  // => List(1, 2, 3, 2, 1) | 
|     54  |     43  | 
|         |     44 // (3) | 
|     55 abstract class Tip |     45 abstract class Tip | 
|     56 case object Absent extends Tip |     46 case object Absent extends Tip | 
|     57 case object Present extends Tip |     47 case object Present extends Tip | 
|     58 case object Correct extends Tip |     48 case object Correct extends Tip | 
|     59  |     49  | 
|     60 def pool(secret: String, word: String) : List[Char]= { |     50  | 
|         |     51 def pool(secret: String, word: String) : List[Char] = { | 
|     61   for (i <- (0 to 4).toList  |     52   for (i <- (0 to 4).toList  | 
|     62        if secret(i) != word(i)) |     53        if secret(i) != word(i)) | 
|     63   yield secret(i)  |     54   yield secret(i)  | 
|     64 } |     55 } | 
|     65  |     56  | 
|     66 def aux(secret: List[Char], word: List[Char], pool: List[Char]) : List[Tip] = (secret, word) match { |     57 def aux(secret: List[Char], word: List[Char], pool: List[Char]) : List[Tip] = (secret, word) match { | 
|     67     case (Nil, Nil) => Nil |     58     case (Nil, Nil) => Nil | 
|     68     case (s::srest, w::wrest) => { |     59     case (s::srest, w::wrest) => { | 
|     69         if (s == w) Correct::aux(srest, wrest, pool) |     60         if (s == w) Correct::aux(srest, wrest, pool) | 
|     70         else if (pool.contains(w)) Present::aux(srest, wrest, removeOne(pool, w)) |     61         else if (pool.contains(w)) Present::aux(srest, wrest, removeN(pool, w, 1)) | 
|     71         else  Absent::aux(srest, wrest, pool) |     62         else  Absent::aux(srest, wrest, pool) | 
|     72     } |     63     } | 
|     73 } |     64 } | 
|     74  |     65  | 
|     75 def score(secret: String, word: String) : List[Tip] = |         | 
|     76     aux(secret.toList, word.toList, pool(secret, word)) |         | 
|     77  |     66  | 
|     78 /* |     67 def score(secret: String, word: String) : List[Tip] =  | 
|     79 def pool(secret: String, word: String) : List[Char] = { |     68   aux(secret.toList, word.toList, pool(secret, word)) | 
|     80 	(secret zip word).collect { |         | 
|     81 		case (c1, c2) if c1 != c2 => s"$c1" |         | 
|     82 	}.toList.flatten |         | 
|     83 } |         | 
|     84  |         | 
|     85 def aux2(secret: String, word: String, pool: List[Char], result: List[Tip]) : List[Tip] = { |         | 
|     86     if (word.length == 0){ |         | 
|     87         result.reverse |         | 
|     88     } |         | 
|     89     else{ |         | 
|     90 		if (word.take(1) == secret.take(1)) { |         | 
|     91 			val updated = Correct :: result	 |         | 
|     92 			aux2(secret.drop(1), word.drop(1), pool, updated) |         | 
|     93 		}	 |         | 
|     94 		else { |         | 
|     95 			if(pool.contains(word.take(1)(0))){ |         | 
|     96 				val updated = Present :: result |         | 
|     97 				val removed = removeOne(pool, word.take(1)(0)).mkString.toList  |         | 
|     98 				aux2(secret.drop(1), word.drop(1), removed, updated) |         | 
|     99 			} |         | 
|    100 			else{ |         | 
|    101 				val updated = Absent :: result |         | 
|    102 				aux2(secret.drop(1), word.drop(1), pool, updated) |         | 
|    103 			} |         | 
|    104 		} |         | 
|    105 	} |         | 
|    106 } |         | 
|    107  |         | 
|    108 def aux(secret: List[Char], word: List[Char], pool: List[Char]) : List[Tip] = { |         | 
|    109     aux2(secret.mkString, word.mkString, pool, List()) |         | 
|    110 } |         | 
|    111  |         | 
|    112 def score(secret: String, word: String) : List[Tip] = { |         | 
|    113     aux(secret.toList, word.toList, pool(secret, word)) |         | 
|    114 } |         | 
|    115 */ |         | 
|    116  |         | 
|    117 // score("chess", "caves") |         | 
|    118 // score("doses", "slide") |         | 
|    119 // score("chess", "swiss") |         | 
|    120 // score("chess", "eexss") |         | 
|    121  |     69  | 
|    122  |     70  | 
|    123 //============================= |     71 // score("chess", "caves") // => List(Correct, Absent, Absent, Present, Correct) | 
|    124 // Task 4 [0.5 Marks] |     72 // score("doses", "slide") // => List(Present, Absent, Absent, Present, Present) | 
|         |     73 // score("chess", "swiss") // => List(Absent, Absent, Absent, Correct, Correct) | 
|         |     74 // score("chess", "eexss") // => List(Present, Absent, Absent, Correct, Correct) | 
|    125  |     75  | 
|         |     76 // (4) | 
|    126 def eval(t: Tip) : Int = t match { |     77 def eval(t: Tip) : Int = t match { | 
|    127     case Correct => 10 |     78     case Correct => 10 | 
|    128     case Present => 1 |     79     case Present => 1 | 
|    129     case Absent => 0 |     80     case Absent => 0 | 
|    130 } |     81 } | 
|    131  |     82  | 
|    132 def iscore(secret: String, word: String) : Int = { |     83 def iscore(secret: String, word: String) : Int =  | 
|    133     val list = score(secret, word) |     84   score(secret, word).map(eval).sum | 
|    134 	val values = list.map(n => eval(n)) |     85  | 
|    135 	values.sum |     86 //iscore("chess", "caves") // => 21 | 
|         |     87 //iscore("chess", "swiss") // => 20 | 
|         |     88  | 
|         |     89 // (5) | 
|         |     90 def lowest(secrets: List[String], word: String, current: Int, acc: List[String]) : List[String] = secrets match { | 
|         |     91     case Nil => acc | 
|         |     92     case s::srest => { | 
|         |     93         val nscore = iscore(s, word) | 
|         |     94         if (nscore < current) lowest(srest, word, nscore, List(s))  | 
|         |     95         else if (nscore == current) lowest(srest, word, current, s::acc) | 
|         |     96         else  lowest(srest, word, current, acc) | 
|         |     97     } | 
|    136 } |     98 } | 
|    137  |     99  | 
|    138 // iscore("chess", "caves") |    100 def evil(secrets: List[String], word: String) =  | 
|    139 // iscore("doses", "slide") |    101   lowest(secrets, word, Int.MaxValue, Nil) | 
|    140 // iscore("chess", "swiss") |         | 
|    141 // iscore("chess", "eexss") |         | 
|    142  |         | 
|    143 //============================= |         | 
|    144 // Task 5 [1.5 Mark] |         | 
|    145  |         | 
|    146 def lowest(secrets: List[String], word: String, current: Int, acc: List[String]) : List[String] = { |         | 
|    147     val mapped = secrets.map(x => (x, iscore(x, word))) |         | 
|    148     val min = mapped.minBy(_._2)._2 |         | 
|    149     val filt = mapped.filter(_._2 == min) |         | 
|    150     filt.map(x => x._1) |         | 
|    151 } |         | 
|    152  |         | 
|    153 def evil(secrets: List[String], word: String) : List[String] = { |         | 
|    154     lowest(secrets, word, Int.MaxValue, List()) |         | 
|    155 } |         | 
|    156  |         | 
|    157 //evil(secrets, "stent").length  |         | 
|    158 //evil(secrets, "hexes").length  |         | 
|    159 //evil(secrets, "horse").length  |         | 
|    160 //evil(secrets, "hoise").length  |         | 
|    161 //evil(secrets, "house").length  |         | 
|    162  |    102  | 
|    163  |    103  | 
|    164 //============================= |    104 //evil(secrets, "stent").length | 
|    165 // Task 6 [1 Mark] |    105 //evil(secrets, "hexes").length | 
|         |    106 //evil(secrets, "horse").length | 
|         |    107 //evil(secrets, "hoise").length | 
|         |    108 //evil(secrets, "house").length | 
|    166  |    109  | 
|         |    110 // (6) | 
|    167 def frequencies(secrets: List[String]) : Map[Char, Double] = { |    111 def frequencies(secrets: List[String]) : Map[Char, Double] = { | 
|    168     val all = secrets.flatten |    112     val all = secrets.flatten | 
|    169     all.groupBy(identity).view.mapValues(1.0D - _.size.toDouble / all.size ).toMap |    113     all.groupBy(identity).view.mapValues(1.0D - _.size.toDouble / all.size ).toMap | 
|    170 } |    114 } | 
|    171  |    115  | 
|    172 //frequencies(secrets)('y') |    116 // (7) | 
|    173  |         | 
|    174 /* |         | 
|    175  |         | 
|    176 def frequencies(secrets: List[String]) : Map[Char, Double] = { |         | 
|    177     val letters = secrets.flatten |         | 
|    178     val totalLetters = letters.length.toDouble |         | 
|    179     val alph = ('a' to 'z').toList |         | 
|    180     val letterCount = alph.map(x => (x, letters.filter(_ == x).length.toDouble)) |         | 
|    181     letterCount.map(x => (x._1, 1.0-(letterCount.filter(_._1 == x._1)(0)._2/totalLetters))).toMap |         | 
|    182 } |         | 
|    183 */ |         | 
|    184  |         | 
|    185 // frequencies(secrets)('y') |         | 
|    186 // frequencies(secrets)('e') |         | 
|    187  |         | 
|    188 //============================= |         | 
|    189 // Task 7 [1 Mark] |         | 
|    190  |         | 
|    191 def rank(frqs: Map[Char, Double], s: String) = { |    117 def rank(frqs: Map[Char, Double], s: String) = { | 
|    192     s.map(frqs(_)).sum |    118     s.map(frqs(_)).sum | 
|    193 } |    119 } | 
|         |    120  | 
|    194  |    121  | 
|    195 def ranked_evil(secrets: List[String], word: String) = { |    122 def ranked_evil(secrets: List[String], word: String) = { | 
|    196     val frqs = frequencies(secrets) |    123     val frqs = frequencies(secrets) | 
|    197     val ev = evil(secrets, word) |    124     val ev = evil(secrets, word) | 
|    198     ev.groupBy(rank(frqs, _)).toList.sortBy(_._1).reverse.head._2 |    125     ev.groupBy(rank(frqs, _)).toList.sortBy(_._1).reverse.head._2 | 
|    199 } |    126 } | 
|    200  |    127  | 
|    201 /* |    128  | 
|    202 def rank(frqs: Map[Char, Double], s: String) : Double = { |         | 
|    203     val letters = s.toList |         | 
|    204     val scores = letters.map(x => frqs(x).toDouble) |         | 
|    205     scores.sum |         | 
|    206 } |    129 } | 
|    207  |    130  | 
|    208 def ranked_evil(secrets: List[String], word: String) : List[String] = { |         | 
|    209     val most = evil(secrets, word) |         | 
|    210     val mapped = most.map(x => (x, rank(frequencies(secrets),x))) |         | 
|    211     val mx = mapped.maxBy(_._2)._2 |         | 
|    212     val filt = mapped.filter(_._2 == mx) |         | 
|    213     filt.map(x => x._1) |         | 
|    214 } |         | 
|    215 */ |         | 
|    216 //rank(frequencies(secrets), "adobe")  |         | 
|    217 //rank(frequencies(secrets), "gaffe")  |         | 
|    218 //rank(frequencies(secrets), "fuzzy")  |         | 
|    219  |    131  | 
|    220 //ranked_evil(secrets, "beats")  |         | 
|    221 //ranked_evil(secrets, "vitae")  |         | 
|    222 //ranked_evil(secrets, "bento")  |         | 
|    223 //ranked_evil(secrets, "belts")  |         | 
|    224  |    132  | 
|    225 } |    133  | 
|         |    134  | 
|         |    135  | 
|         |    136 // This template code is subject to copyright  | 
|         |    137 // by King's College London, 2022. Do not  | 
|         |    138 // make the template code public in any shape  | 
|         |    139 // or form, and do not exchange it with other  | 
|         |    140 // students under any circumstance. |