I have come across a most troubling bug in my code below. The buttons Map mutates even though I pass it as an immutable Map. The keys remain the same, and the map points to two immutable Ints, yet below you can see the map clearly has different values during the run. I am absolutely stumped and have no idea what is happening.
def makeTrace(trace : List[(String)], buttons : Map[String, (Int,Int)],
outputScreen : ScreenRegion, hashMap : Map[Array[Byte], String])
: (List[(String,String)], Map[Array[Byte], String]) = {
println(buttons.toString)
//clearing the device
val clear = buttons.getOrElse("clear", throw new Exception("Clear Not Found"))
//clear.circle(3000)
val thisButton = new ScreenLocation(clear._1, clear._2)
click(thisButton)
//updates the map and returns a list of (transition, state)
trace.foldLeft((Nil : List[(String,String)], hashMap))( (list, trace) => {
println(buttons.toString)
val transition : String = trace
val location = buttons.getOrElse(transition, throw new Exception("whatever"))
val button = new ScreenLocation(location._1, location._2)
button.circle(500)
button.label(transition, 500)
click(button)
//reading and hashing
pause(500)
val capturedImage : BufferedImage = outputScreen.capture()
val outputStream : ByteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(capturedImage, "png", outputStream)
val byte : Array[Byte] = outputStream.toByteArray();
//end hash
//if our state exists for the hash
if (hashMap.contains(byte)){ list match {
case (accumulator, map) => ((transition , hashMap.getOrElse(byte, throw new Exception("Our map broke if"))):: accumulator, map)
}
//if we need to update the map
}else list match {
case (accumulator, map) => {
//adding a new state based on the maps size
val newMap : Map[Array[Byte], String] = map + ((byte , "State" + map.size.toString))
val imageFile : File = new File("State" + map.size.toString + ".png");
ImageIO.write(capturedImage, "png", imageFile);
((transition, newMap.getOrElse(byte, throw new Exception("Our map broke else"))) :: accumulator, newMap)
}
}
})
}
Right before I call this function I initialize the map to an immutable map that points to immutable objects.
val buttons = makeImmutable(MutButtons)
val traceAndMap = TraceFinder.makeTrace(("clear" ::"five"::"five"::"minus"::"five"::"equals":: Nil), buttons, outputScreen, Map.empty)
Where makeImmutable is
def makeImmutable(buttons : Map[String, (Int,Int)]) : Map[String, (Int,Int)] = {
buttons.mapValues(button => button match {
case (x, y) =>
val newX = x
val newY = y
(newX,newY)
})
}
Here is the output, you can see the state change for clear, minus, and five
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (842,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (842,377))
Map(equals -> (959,425), clear -> (959,345), minus -> (920,409), five -> (842,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (842,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (881,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (842,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,441), five -> (842,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (881,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (842,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (842,377))