Skip to content

Commit

Permalink
some mint action conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
robertfmurdock committed Jul 18, 2023
1 parent 4a36384 commit f722d59
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 175 deletions.
1 change: 1 addition & 0 deletions libraries/action/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dependencies {
commonTestImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json")
commonTestImplementation("org.jetbrains.kotlin:kotlin-test")
commonTestImplementation("com.zegreatrob.testmints:standard")
commonTestImplementation("com.zegreatrob.testmints:async")
commonTestImplementation("com.zegreatrob.testmints:minassert")

"jvmMainImplementation"(kotlin("reflect"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,79 +9,78 @@ import com.zegreatrob.coupling.model.pairassignmentdocument.withPins
import com.zegreatrob.coupling.model.pin.Pin
import com.zegreatrob.coupling.model.pin.PinTarget
import com.zegreatrob.coupling.model.player.Player
import com.zegreatrob.testmints.action.async.SimpleSuspendAction
import com.zegreatrob.testmints.action.annotation.ActionMint
import kotools.types.collection.NotEmptyList

@ActionMint
data class AssignPinsAction(
val pairs: NotEmptyList<CouplingPair>,
val pins: List<Pin>,
val history: List<PairAssignmentDocument>,
) : SimpleSuspendAction<AssignPinsActionDispatcher, NotEmptyList<PinnedCouplingPair>> {
override val performFunc = link(AssignPinsActionDispatcher::perform)
}

interface AssignPinsActionDispatcher {
fun perform(action: AssignPinsAction): NotEmptyList<PinnedCouplingPair> {
var pinnedPairs = action.pairs.map { it.withPins() }

action.pins.filter { it.target == PinTarget.Pair }
.forEach { pin ->
val pinIterator = listOf(pin).iterator()

val candidatePairs = findPairCandidates(pin, pinnedPairs, action.history)

pinnedPairs = pinnedPairs.map { pair ->
if (candidatePairs.contains(pair) && pinIterator.hasNext()) {
pair.copy(pins = pair.pins + pinIterator.next())
} else {
pair
) {
interface Dispatcher {
suspend fun perform(action: AssignPinsAction): NotEmptyList<PinnedCouplingPair> {
var pinnedPairs = action.pairs.map { it.withPins() }

action.pins.filter { it.target == PinTarget.Pair }
.forEach { pin ->
val pinIterator = listOf(pin).iterator()

val candidatePairs = findPairCandidates(pin, pinnedPairs, action.history)

pinnedPairs = pinnedPairs.map { pair ->
if (candidatePairs.contains(pair) && pinIterator.hasNext()) {
pair.copy(pins = pair.pins + pinIterator.next())
} else {
pair
}
}
}
}

return pinnedPairs
}

private fun findPairCandidates(
pin: Pin,
pinnedPairs: NotEmptyList<PinnedCouplingPair>,
history: List<PairAssignmentDocument>,
): List<PinnedCouplingPair> {
val pairsGroupedByLastTime = pinnedPairs.toList().groupBy { pair ->
lastTimePlayerInPairHadPin(pin, history, pair.players)
return pinnedPairs
}

val candidatePairsWhoNeverHadPin = pairsGroupedByLastTime[-1]
?.candidatesWithFewestPins()
if (candidatePairsWhoNeverHadPin != null) {
return candidatePairsWhoNeverHadPin
}
private fun findPairCandidates(
pin: Pin,
pinnedPairs: NotEmptyList<PinnedCouplingPair>,
history: List<PairAssignmentDocument>,
): List<PinnedCouplingPair> {
val pairsGroupedByLastTime = pinnedPairs.toList().groupBy { pair ->
lastTimePlayerInPairHadPin(pin, history, pair.players)
}

return pairsGroupedByLastTime.minKeyValue()
?.candidatesWithFewestPins()
?: emptyList()
}
val candidatePairsWhoNeverHadPin = pairsGroupedByLastTime[-1]
?.candidatesWithFewestPins()
if (candidatePairsWhoNeverHadPin != null) {
return candidatePairsWhoNeverHadPin
}

return pairsGroupedByLastTime.minKeyValue()
?.candidatesWithFewestPins()
?: emptyList()
}

private fun List<PinnedCouplingPair>.candidatesWithFewestPins() = groupBy { it.pins.count() }.minKeyValue()
private fun List<PinnedCouplingPair>.candidatesWithFewestPins() = groupBy { it.pins.count() }.minKeyValue()

private fun lastTimePlayerInPairHadPin(
pin: Pin,
history: List<PairAssignmentDocument>,
players: NotEmptyList<Player>,
) = history.indexOfFirst { pairAssignmentDocument ->
val pairWithPinPlayers = pairWithPinPlayers(pairAssignmentDocument, pin)
players.toList().fold(false) { foundOne, player ->
foundOne || pairWithPinPlayers?.toList()?.contains(player) == true
private fun lastTimePlayerInPairHadPin(
pin: Pin,
history: List<PairAssignmentDocument>,
players: NotEmptyList<Player>,
) = history.indexOfFirst { pairAssignmentDocument ->
val pairWithPinPlayers = pairWithPinPlayers(pairAssignmentDocument, pin)
players.toList().fold(false) { foundOne, player ->
foundOne || pairWithPinPlayers?.toList()?.contains(player) == true
}
}
}

private fun Map<Int, List<PinnedCouplingPair>>.minKeyValue() = this[keys.minOrNull()]
private fun Map<Int, List<PinnedCouplingPair>>.minKeyValue() = this[keys.minOrNull()]

private fun pairWithPinPlayers(doc: PairAssignmentDocument, pin: Pin) = playersWithPin(doc, pin)
private fun pairWithPinPlayers(doc: PairAssignmentDocument, pin: Pin) = playersWithPin(doc, pin)

private fun playersWithPin(doc: PairAssignmentDocument, pin: Pin) = pairWithPin(doc, pin)
?.players
private fun playersWithPin(doc: PairAssignmentDocument, pin: Pin) = pairWithPin(doc, pin)
?.players

private fun pairWithPin(pairAssignmentDocument: PairAssignmentDocument, pin: Pin) =
pairAssignmentDocument.pairs.toList().find { docPair -> docPair.pins.contains(pin) }
private fun pairWithPin(pairAssignmentDocument: PairAssignmentDocument, pin: Pin) =
pairAssignmentDocument.pairs.toList().find { docPair -> docPair.pins.contains(pin) }
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
package com.zegreatrob.coupling.action.heatmap

import com.zegreatrob.coupling.action.pairassignmentdocument.AssignPinsAction
import com.zegreatrob.coupling.action.pairassignmentdocument.AssignPinsActionDispatcher
import com.zegreatrob.coupling.action.stats.heatmap.CalculateHeatMapAction
import com.zegreatrob.coupling.model.pairassignmentdocument.PairAssignmentDocument
import com.zegreatrob.coupling.model.pairassignmentdocument.PairAssignmentDocumentId
import com.zegreatrob.coupling.model.pairassignmentdocument.pairOf
import com.zegreatrob.coupling.model.player.Player
import com.zegreatrob.minassert.assertIsEqualTo
import com.zegreatrob.testmints.async.asyncSetup
import com.zegreatrob.testmints.setup
import kotlinx.datetime.Clock
import kotools.types.collection.notEmptyListOf
import kotlin.test.Test

class CalculateHeatMapCommandTest {

companion object :
CalculateHeatMapAction.Dispatcher,
AssignPinsActionDispatcher {
private fun pairAssignmentDocument(player1: Player, player2: Player) =
companion object : CalculateHeatMapAction.Dispatcher, AssignPinsAction.Dispatcher {
private suspend fun pairAssignmentDocument(player1: Player, player2: Player) =
PairAssignmentDocument(
id = PairAssignmentDocumentId(""),
date = Clock.System.now(),
Expand Down Expand Up @@ -81,16 +79,17 @@ class CalculateHeatMapCommandTest {
}

@Test
fun withTwoPlayersAndShortHistoryProducesTwoRowsWithHeatValues() = setup(object {
fun withTwoPlayersAndShortHistoryProducesTwoRowsWithHeatValues() = asyncSetup(object {
val players = listOf(
Player(id = "0", avatarType = null),
Player(id = "1", avatarType = null),
)
val history = listOf(pairAssignmentDocument(players[0], players[1]))
val rotationPeriod = 1
val action =
CalculateHeatMapAction(players, history, rotationPeriod)
}) exercise {
lateinit var action: CalculateHeatMapAction
}) {
val history = listOf(pairAssignmentDocument(players[0], players[1]))
action = CalculateHeatMapAction(players, history, rotationPeriod)
} exercise {
perform(action)
} verify { result ->
result.assertIsEqualTo(
Expand All @@ -102,22 +101,23 @@ class CalculateHeatMapCommandTest {
}

@Test
fun withTwoPlayersAndFullHistoryProducesTwoRowsWithHeatValues() = setup(object {
fun withTwoPlayersAndFullHistoryProducesTwoRowsWithHeatValues() = asyncSetup(object {
val players = listOf(
Player(id = "0", avatarType = null),
Player(id = "1", avatarType = null),
)
val rotationPeriod = 1
lateinit var action: CalculateHeatMapAction
}) {
val history = listOf(
pairAssignmentDocument(players[0], players[1]),
pairAssignmentDocument(players[0], players[1]),
pairAssignmentDocument(players[0], players[1]),
pairAssignmentDocument(players[0], players[1]),
pairAssignmentDocument(players[0], players[1]),
)
val rotationPeriod = 1
val action =
CalculateHeatMapAction(players, history, rotationPeriod)
}) exercise {
action = CalculateHeatMapAction(players, history, rotationPeriod)
} exercise {
perform(action)
} verify { result ->
result.assertIsEqualTo(
Expand All @@ -129,12 +129,15 @@ class CalculateHeatMapCommandTest {
}

@Test
fun withThreePlayersAndInterestingHistoryProducesThreeRowsWithHeatValues() = setup(object {
fun withThreePlayersAndInterestingHistoryProducesThreeRowsWithHeatValues() = asyncSetup(object {
val players = listOf(
Player(id = "0", avatarType = null),
Player(id = "1", avatarType = null),
Player(id = "2", avatarType = null),
)
val rotationPeriod = 3
lateinit var action: CalculateHeatMapAction
}) {
val history = listOf(
pairAssignmentDocument(players[0], players[1]),
pairAssignmentDocument(players[0], players[1]),
Expand All @@ -152,10 +155,8 @@ class CalculateHeatMapCommandTest {
pairAssignmentDocument(players[0], players[1]),
pairAssignmentDocument(players[0], players[2]),
)
val rotationPeriod = 3
val action =
CalculateHeatMapAction(players, history, rotationPeriod)
}) exercise {
action = CalculateHeatMapAction(players, history, rotationPeriod)
} exercise {
perform(action)
} verify { result ->
result.assertIsEqualTo(
Expand Down
Loading

0 comments on commit f722d59

Please sign in to comment.