treasure/display.nim

218 lines
5.6 KiB
Nim

import input_type
import map_type
import turtle
import turtle_type
import math
import sequtils
import strutils
import strformat
import terminal
import algorithm
const blueCutoff = 0.05
const cyanCutoff = 0.1
const greenCutoff = 0.205
const yellowCutoff = 0.335
const redCutoff = 0.505
const magentaCutoff = 1.0
proc setHotnessBackground(hotness: float, maxHotness: float) =
if hotness == 0:
stdout.setBackgroundColor(bgDefault)
else:
let hotnessFrac = hotness / maxHotness
if hotnessFrac < blueCutoff:
stdout.setBackgroundColor(bgBlue)
elif hotnessFrac < cyanCutoff:
stdout.setBackgroundColor(bgCyan)
elif hotnessFrac < greenCutoff:
stdout.setBackgroundColor(bgGreen)
elif hotnessFrac < yellowCutoff:
stdout.setBackgroundColor(bgYellow)
elif hotnessFrac < redCutoff:
stdout.setBackgroundColor(bgRed)
else:
stdout.setBackgroundColor(bgMagenta)
proc printHotmapLegend(maxHotness: float, x,y: int) =
template line(num: int) =
setCursorPos(x, y + num - 1)
template showColor(col: BackgroundColor) =
stdout.setBackgroundColor(col)
stdout.write(" ")
stdout.setBackgroundColor(bgBlack)
stdout.write(" ")
proc writeNumber(num: float) =
stdout.write(fmt"{num.int()}")
line(1)
showColor(bgDefault)
writeNumber(0.0)
line(2)
showColor(bgBlue)
writeNumber(blueCutoff * maxHotness)
line(3)
showColor(bgCyan)
writeNumber(cyanCutoff * maxHotness)
line(4)
showColor(bgGreen)
writeNumber(greenCutoff * maxHotness)
line(5)
showColor(bgYellow)
writeNumber(yellowCutoff * maxHotness)
line(6)
showColor(bgRed)
writeNumber(redCutoff * maxHotness)
line(7)
showColor(bgMagenta)
writeNumber(magentaCutoff * maxHotness)
proc displayGeneration*(generationNumber: int, timeSeconds: float, seed: int, input: Input, map: Map, turtles: var seq[Turtle]) =
# SCREEN INIT
stdout.setBackgroundColor(bgBlack)
stdout.setForegroundColor(fgWhite)
eraseScreen()
setCursorPos(0, 0)
# STATISTICS
let scoreCount = turtles.len()
var scoreSum = 0
var stepsSum = 0
for turtle in turtles:
scoreSum += turtle.score
stepsSum += turtle.steps
let scoreAvg = scoreSum.float() / scoreCount.float()
let stepsAvg = stepsSum.float() / scoreCount.float()
let variance = turtles.mapIt((it.score.float() - scoreAvg)^2).sum() / scoreCount.float()
let stddev = variance.sqrt()
# PRINT BASIC INFO
echo &"Generation number: {generationNumber}"
echo &"Computation time since last stop: {timeSeconds} s"
echo &"Seed: {seed}"
echo &"Population: {turtles.len()} turtles"
echo &"Mean score: {scoreAvg} (in {stepsAvg} steps)"
echo &"Standard deviation: {stddev}"
echo &"Top score: {turtles[0].score} (in {turtles[0].steps} steps)"
# PERCENTILS
turtles.sort(compareTurtles)
var percentils: seq[int] = @[]
for i in 1..9:
let index = (input.population div 10) * i
percentils.add(turtles[index].score)
let percentilsString = percentils.join(", ")
echo &"Score of every 10%th percentil: {percentilsString}"
# CHOOSE NOTABLE
var top10Paths: seq[Turtle] = @[]
var notables: seq[int] = @[]
var i = 0
while true:
if i > turtles.high():
break
if top10Paths.len() == 0 or top10Paths[top10Paths.high()].score != turtles[i].score:
top10Paths.add(turtles[i])
notables.add(i)
if top10Paths.len() >= 10:
break
inc i
let notableString = notables.join(", ")
echo &"Notable turtles: {notableString}"
# HOTNESS MAP
var hotness: seq[seq[int]] = @[]
var top10Hot: seq[seq[int]] = @[]
var topHot: seq[seq[int]] = @[]
for i in 0..input.size-1:
hotness.add(@[])
top10Hot.add(@[])
topHot.add(@[])
for j in 0..input.size-1:
hotness[i].add(0)
top10Hot[i].add(0)
topHot[i].add(0)
template populateHotness(turtles: seq[Turtle], hotSeq: var seq[seq[int]], maxHot: var int) =
for turtle in turtles:
let path = turtle.path
for i in 0..path.xs.high():
let x = path.xs[i]
let y = path.ys[i]
hotSeq[y][x].inc()
if hotSeq[y][x] > maxHot:
maxHot = hotSeq[y][x]
var maxHotness = 0
populateHotness(turtles, hotness, maxHotness)
var maxTop10Hotness = 0
populateHotness(top10Paths, top10Hot, maxTop10Hotness)
var maxTopHotness = 0
populateHotness(@[turtles[0]], topHot, maxTopHotness)
# DISPLAY HOTNESS MAP
echo "Hotmap of All/Notable/Best Turtles"
const yOffset = 10
let xOffset = input.size + 2
for y in 0..input.size-1:
for x in 0..input.size-1:
let hotness = hotness[y][x]
let top10hotness = top10Hot[y][x]
let topHotness = topHot[y][x]
let symbol = map[y][x].tileToString()
# hotmap
setHotnessBackground(hotness.float(), maxHotness.float())
setCursorPos(x, y+yOffset)
stdout.write(symbol)
# top10 map
setHotnessBackground(top10hotness.float(), maxTop10Hotness.float())
setCursorPos(x+xOffset, y+yOffset)
stdout.write(symbol)
# top map
setHotnessBackground(topHotness.float(), maxTopHotness.float())
setCursorPos(x+2*xOffset, y+yOffset)
stdout.write(symbol)
stdout.setBackgroundColor(bgBlack)
stdout.write("\n")
echo ""
# display legend
let legendYOffset = yOffset + 1 + input.size
printHotmapLegend(maxHotness.float(), 0, legendYOffset)
printHotmapLegend(maxTop10Hotness.float(), xOffset, legendYOffset)
printHotmapLegend(maxTopHotness.float(), 2*xOffset, legendYOffset)
echo ""
echo ""
# FINISH
echo "Press enter to continue."
discard stdin.readLine()
stdout.setBackgroundColor(bgDefault)
eraseScreen()
setCursorPos(1,1)
echo ""