blob: b8e1f4509ab6f329eb91bad4400fccb8d6dd2a45 [file] [log] [blame]
/*
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package benchmarks.flow.scrabble
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import org.openjdk.jmh.annotations.*
import java.lang.Long.max
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.*
@Warmup(iterations = 7, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 7, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(value = 1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
open class FlowPlaysScrabbleBase : ShakespearePlaysScrabble() {
@Benchmark
public override fun play(): List<Map.Entry<Int, List<String>>> {
val scoreOfALetter = { letter: Int -> flowOf(letterScores[letter - 'a'.toInt()]) }
val letterScore = { entry: Map.Entry<Int, LongWrapper> ->
flowOf(
letterScores[entry.key - 'a'.toInt()] * Integer.min(
entry.value.get().toInt(),
scrabbleAvailableLetters[entry.key - 'a'.toInt()]
)
)
}
val toIntegerStream = { string: String ->
IterableSpliterator.of(string.chars().boxed().spliterator()).asFlow()
}
val histoOfLetters = { word: String ->
flow {
emit(toIntegerStream(word).fold(HashMap<Int, LongWrapper>()) { accumulator, value ->
var newValue: LongWrapper? = accumulator[value]
if (newValue == null) {
newValue = LongWrapper.zero()
}
accumulator[value] = newValue.incAndSet()
accumulator
})
}
}
val blank = { entry: Map.Entry<Int, LongWrapper> ->
flowOf(max(0L, entry.value.get() - scrabbleAvailableLetters[entry.key - 'a'.toInt()]))
}
val nBlanks = { word: String ->
flow {
emit(histoOfLetters(word)
.flatMapConcat { map -> map.entries.iterator().asFlow() }
.flatMapConcat({ blank(it) })
.reduce { a, b -> a + b })
}
}
val checkBlanks = { word: String ->
nBlanks(word).flatMapConcat { l -> flowOf(l <= 2L) }
}
val score2 = { word: String ->
flow {
emit(histoOfLetters(word)
.flatMapConcat { map -> map.entries.iterator().asFlow() }
.flatMapConcat { letterScore(it) }
.reduce { a, b -> a + b })
}
}
val first3 = { word: String ->
IterableSpliterator.of(word.chars().boxed().limit(3).spliterator()).asFlow()
}
val last3 = { word: String ->
IterableSpliterator.of(word.chars().boxed().skip(3).spliterator()).asFlow()
}
val toBeMaxed = { word: String -> flowOf(first3(word), last3(word)).flattenConcat() }
// Bonus for double letter
val bonusForDoubleLetter = { word: String ->
flow {
emit(toBeMaxed(word)
.flatMapConcat { scoreOfALetter(it) }
.reduce { a, b -> max(a, b) })
}
}
val score3 = { word: String ->
flow {
emit(flowOf(
score2(word), score2(word),
bonusForDoubleLetter(word),
bonusForDoubleLetter(word),
flowOf(if (word.length == 7) 50 else 0)
).flattenConcat().reduce { a, b -> a + b })
}
}
val buildHistoOnScore: (((String) -> Flow<Int>) -> Flow<TreeMap<Int, List<String>>>) = { score ->
flow {
emit(shakespeareWords.asFlow()
.filter({ scrabbleWords.contains(it) && checkBlanks(it).single() })
.fold(TreeMap<Int, List<String>>(Collections.reverseOrder())) { acc, value ->
val key = score(value).single()
var list = acc[key] as MutableList<String>?
if (list == null) {
list = ArrayList()
acc[key] = list
}
list.add(value)
acc
})
}
}
return runBlocking {
buildHistoOnScore(score3)
.flatMapConcat { map -> map.entries.iterator().asFlow() }
.take(3)
.toList()
}
}
}