// Resit Part about an Evil Wordle Clone
//==========================================
//
// Task description are on KEATS
//
// Make sure you use Scala Version 2.13 (not Scala 3)
//
// Upload your submision to OneSpace and give Senir and Christian
// write access to the folder. The folder should have you email
// address as name. Give us write permission by July 25th such
// that we can download your solution by the closing date August 4th.
object Resit {
import io.Source
import scala.util._
//=============================
// Task 1 [0.25 Marks]
def get_wordle_list(url: String) : List[String] = {
try{
val raw = Source.fromURL(url).mkString
raw.split("\n").toList
}
catch {
case e : Exception => List[String]()
}
}
// val secrets = get_wordle_list("https://nms.kcl.ac.uk/christian.urban/wordle.txt")
// secrets.length => 12972
// secrets.filter(_.length != 5) => Nil
//=============================
// Task 2 [0.25 Marks]
def removeOne[A](xs: List[A], elem: A) : List[A] = {
val index = xs.indexOf(elem)
val first = xs.take(index)
val second = xs.drop(index + 1)
first ::: second
}
// removeOne(List(1,2,3,2,1), 3) => List(1, 2, 2, 1)
// removeOne(List(1,2,3,2,1), 2) => List(1, 3, 2, 1)
// removeOne(List(1,2,3,2,1), 1) => List(2, 3, 2, 1)
// removeOne(List(1,2,3,2,1), 0) => List(1, 2, 3, 2, 1)
//=============================
// Task 3 [1.5 Marks]
abstract class Tip
case object Absent extends Tip
case object Present extends Tip
case object Correct extends Tip
def pool(secret: String, word: String) : List[Char]= {
for (i <- (0 to 4).toList
if secret(i) != word(i))
yield secret(i)
}
def aux(secret: List[Char], word: List[Char], pool: List[Char]) : List[Tip] = (secret, word) match {
case (Nil, Nil) => Nil
case (s::srest, w::wrest) => {
if (s == w) Correct::aux(srest, wrest, pool)
else if (pool.contains(w)) Present::aux(srest, wrest, removeOne(pool, w))
else Absent::aux(srest, wrest, pool)
}
}
def score(secret: String, word: String) : List[Tip] =
aux(secret.toList, word.toList, pool(secret, word))
/*
def pool(secret: String, word: String) : List[Char] = {
(secret zip word).collect {
case (c1, c2) if c1 != c2 => s"$c1"
}.toList.flatten
}
def aux2(secret: String, word: String, pool: List[Char], result: List[Tip]) : List[Tip] = {
if (word.length == 0){
result.reverse
}
else{
if (word.take(1) == secret.take(1)) {
val updated = Correct :: result
aux2(secret.drop(1), word.drop(1), pool, updated)
}
else {
if(pool.contains(word.take(1)(0))){
val updated = Present :: result
val removed = removeOne(pool, word.take(1)(0)).mkString.toList
aux2(secret.drop(1), word.drop(1), removed, updated)
}
else{
val updated = Absent :: result
aux2(secret.drop(1), word.drop(1), pool, updated)
}
}
}
}
def aux(secret: List[Char], word: List[Char], pool: List[Char]) : List[Tip] = {
aux2(secret.mkString, word.mkString, pool, List())
}
def score(secret: String, word: String) : List[Tip] = {
aux(secret.toList, word.toList, pool(secret, word))
}
*/
// score("chess", "caves")
// score("doses", "slide")
// score("chess", "swiss")
// score("chess", "eexss")
//=============================
// Task 4 [0.5 Marks]
def eval(t: Tip) : Int = t match {
case Correct => 10
case Present => 1
case Absent => 0
}
def iscore(secret: String, word: String) : Int = {
val list = score(secret, word)
val values = list.map(n => eval(n))
values.sum
}
// iscore("chess", "caves")
// iscore("doses", "slide")
// iscore("chess", "swiss")
// iscore("chess", "eexss")
//=============================
// Task 5 [1.5 Mark]
def lowest(secrets: List[String], word: String, current: Int, acc: List[String]) : List[String] = {
val mapped = secrets.map(x => (x, iscore(x, word)))
val min = mapped.minBy(_._2)._2
val filt = mapped.filter(_._2 == min)
filt.map(x => x._1)
}
def evil(secrets: List[String], word: String) : List[String] = {
lowest(secrets, word, Int.MaxValue, List())
}
//evil(secrets, "stent").length
//evil(secrets, "hexes").length
//evil(secrets, "horse").length
//evil(secrets, "hoise").length
//evil(secrets, "house").length
//=============================
// Task 6 [1 Mark]
def frequencies(secrets: List[String]) : Map[Char, Double] = {
val all = secrets.flatten
all.groupBy(identity).view.mapValues(1.0D - _.size.toDouble / all.size ).toMap
}
//frequencies(secrets)('y')
/*
def frequencies(secrets: List[String]) : Map[Char, Double] = {
val letters = secrets.flatten
val totalLetters = letters.length.toDouble
val alph = ('a' to 'z').toList
val letterCount = alph.map(x => (x, letters.filter(_ == x).length.toDouble))
letterCount.map(x => (x._1, 1.0-(letterCount.filter(_._1 == x._1)(0)._2/totalLetters))).toMap
}
*/
// frequencies(secrets)('y')
// frequencies(secrets)('e')
//=============================
// Task 7 [1 Mark]
def rank(frqs: Map[Char, Double], s: String) = {
s.map(frqs(_)).sum
}
def ranked_evil(secrets: List[String], word: String) = {
val frqs = frequencies(secrets)
val ev = evil(secrets, word)
ev.groupBy(rank(frqs, _)).toList.sortBy(_._1).reverse.head._2
}
/*
def rank(frqs: Map[Char, Double], s: String) : Double = {
val letters = s.toList
val scores = letters.map(x => frqs(x).toDouble)
scores.sum
}
def ranked_evil(secrets: List[String], word: String) : List[String] = {
val most = evil(secrets, word)
val mapped = most.map(x => (x, rank(frequencies(secrets),x)))
val mx = mapped.maxBy(_._2)._2
val filt = mapped.filter(_._2 == mx)
filt.map(x => x._1)
}
*/
//rank(frequencies(secrets), "adobe")
//rank(frequencies(secrets), "gaffe")
//rank(frequencies(secrets), "fuzzy")
//ranked_evil(secrets, "beats")
//ranked_evil(secrets, "vitae")
//ranked_evil(secrets, "bento")
//ranked_evil(secrets, "belts")
}