Ver código fonte

collect coins algorithm in progress

κρμγ 5 anos atrás
pai
commit
df87f042e6

+ 72 - 9
src/main/kotlin/in/ocsf/bee/freigeld/core/demo/DemoInMem.kt

@@ -45,19 +45,30 @@ class DemoInMem {
 
         bank.exchange(bank.getSelfAccount(), emitter, 1_300_000)
         (0 until 10).forEach { personMap.computeIfAbsent(UUID.randomUUID()) { id -> DemoHuman(id, Nomen.randomName()) } }
-        personMap.keys.map { bank.addAccount(it) }.subList(0, 1).forEach {
+        personMap.keys.map { bank.addAccount(it) }.subList(0, 10).forEach {
             bank.exchange(it, bank.getSelfAccount(), 1_200 + (500 * Math.random()).roundToLong())
                     .thenRun({ log.info("exchange ok") })
                     .exceptionally({ log.error("exchange error"); null })
         }
     }
 
+    private fun doRand0() {
+        val fromPerson = personMap.keys.random()
+        val toPerson = personMap.keys.subtract(setOf(fromPerson)).random()
+        val fromAccount = bank.getAccounts(fromPerson).first()
+        val toAccout = bank.getAccounts(toPerson).first()
+        val dir = if (fromAccount.overall >= toAccout.overall) fromAccount to toAccout else toAccout to fromAccount
+        val x = Math.min(Math.round(dir.first.overall * 0.9), Math.max(0, Math.round(dir.first.overall * Math.random())))
+        bank.exchange(dir.second, dir.first, x)
+    }
+
     @Scheduled(initialDelay = 10 * 1000L, fixedDelay = 5 * 1000L)
     fun tick() {
+        doRand0()
         log.info("tick")
     }
 
-    @Scheduled(initialDelay = 60 * 1000L, fixedDelay = 60 * 1000L)
+    //@Scheduled(initialDelay = 60 * 1000L, fixedDelay = 60 * 1000L)
     fun calc() {
         emitter.calc()
     }
@@ -143,7 +154,7 @@ class GlobalBank : Bank {
                         } else if (e.from.id == selfAccount.id) {
                             false
                         } else {
-                            TODO()
+                            false
                         }
                     } else {
                         true
@@ -152,7 +163,10 @@ class GlobalBank : Bank {
                         e.to.accept(coinsExact)
                         if (e.to.id != selfAccount.id) emitter.free(coinsExact)
                         globalQueue.offerLast(ExchangeSuccessEvent(UUID.randomUUID(), e))
-                    } else {
+                    } else if (e.from.id == selfAccount.id) {
+                        if (e.retry)
+                            throw IllegalArgumentException("already handled event retry, failed")
+                        e.retry = true
                         e.from.accept(coinsExact)
                         if (coinsCashback.isNotEmpty()) {
                             val invCoinsCashback = coinsCashback.map { it as DemoCashbackCoin }.map { it.invert() }
@@ -163,6 +177,25 @@ class GlobalBank : Bank {
                                 e.from.accept(listOf(coin))
                             }
                         }
+                    } else {
+                        val fromFake: BankAccount = DemoSelfAccount(UUID.randomUUID())
+                        fromFake.accept(coinsExact)
+                        log.info("${fromFake} hold ${fromFake.overall} from ${e.from}")
+                        val invCoins = coinsCashback.map { it as DemoCashbackCoin }.map { it.invert() }.map {
+                            val coinId = emitter.emitOne(it)
+                            emitter.extractOne(coinId)
+                        }
+                        val backAmount = fromFake.overall - e.amount
+                        fromFake.accept(invCoins)
+                        val extraCoins = fromFake.extractMoreOrExact(e.amount)
+                        val backCoinsCashback = fromFake.extractMoreOrExact(backAmount)
+                        val backInvCoins = backCoinsCashback.filter { it.id == null }.map { it as DemoCashbackCoin }.map { it.invert() }.map {
+                            val coinId = emitter.emitOne(it)
+                            emitter.extractOne(coinId)
+                        }
+                        e.from.accept(extraCoins + backInvCoins)
+                        selfAccount.accept(backCoinsCashback.filter { it.id != null })
+                        ok
                     }
                     ok
                 }
@@ -259,7 +292,7 @@ class ExchangeFailedEvent(eventId: UUID, val parentEvent: ExchangeStartEvent, va
 
 class ExchangeFailedException(val event: ExchangeBankEvent, message: String = "exchange failed") : Exception(message)
 
-class ExchangeStartEvent(eventId: UUID, val to: BankAccount, val from: BankAccount, val amount: Long) : ExchangeBankEvent(eventId)
+class ExchangeStartEvent(eventId: UUID, val to: BankAccount, val from: BankAccount, val amount: Long, var retry: Boolean = false) : ExchangeBankEvent(eventId)
 
 @Service
 class DemoInMemEmitter : GlobalEmitter {
@@ -505,6 +538,8 @@ class CoinUtils {
 
     companion object {
 
+        val avaiues = CoinValue.values().flatMap { value -> (0..0/*(maxEraValue - 1)*/).map { era -> value to era to (value.amount - (era * value.delta)) } }.map { it.second to it.first }.toMap()
+
         fun makeSomeFake(cashback: Long): Collection<Coin> {
             val ret = mutableListOf<Coin>()
             var totalCashback = cashback
@@ -538,10 +573,18 @@ class CoinUtils {
                 } else {
                     val smallestGt = leastCoinMap.keys.sorted().firstOrNull()
                     if (smallestGt != null) {
-                        val cashbackAmount = diffAmount - smallestGt
-                        totalAmount += smallestGt
-                        val coinId = leastCoinMap.get(smallestGt)?.first()!!
-                        usedCoins.add(coinId)
+                        val cashbackAmount = if (smallestGt >= amount && smallestGt > totalAmount) {
+                            usedCoins.clear() //одной монетой перекрываем всё предыдущее
+                            totalAmount = smallestGt
+                            val coinId = leastCoinMap.get(smallestGt)?.first()!!
+                            usedCoins.add(coinId)
+                            amount - smallestGt
+                        } else {
+                            totalAmount += smallestGt
+                            val coinId = leastCoinMap.get(smallestGt)?.first()!!
+                            usedCoins.add(coinId)
+                            diffAmount - smallestGt
+                        }
                         ret.addAll(makeSomeFake(cashbackAmount))
                     } else {
                         TODO()
@@ -567,6 +610,26 @@ class CoinUtils {
             val neg = sum(coins.filter { it.id == null })
             return if (neg != 0L) "${pos + neg}(${pos}${neg})" else "$pos"
         }
+
+
+        fun makeSomeVariants(x: Long): List<Map<Pair<CoinValue, Int>, Int>> {
+            val baseValues = avaiues.keys
+            val variants: MutableSet<Set<Long>> = mutableSetOf()
+            var calcFn: ((excl: Set<Long>) -> Unit)? = null
+
+            calcFn = { skip ->
+                val checkValues = baseValues - skip
+                if (checkValues.isNotEmpty()) variants.add(checkValues)
+                checkValues.forEach { x -> calcFn!!(skip + setOf(x)) }
+            }
+
+            return if (x > 0L) {
+                calcFn(setOf())
+                return listOf()
+            } else {
+                return listOf()
+            }
+        }
     }
 }
 

+ 9 - 0
src/test/kotlin/`in`/ocsf/bee/freigeld/core/demo/CoinUtilsTest.kt

@@ -4,6 +4,7 @@ import `in`.ocsf.bee.freigeld.core.model.CoinValue
 import org.junit.jupiter.api.Test
 import java.util.*
 import kotlin.test.assertEquals
+import kotlin.test.assertFalse
 
 class CoinUtilsTest {
 
@@ -24,4 +25,12 @@ class CoinUtilsTest {
         assertEquals(fromAccount.overall, CoinValue.one.amount)
     }
 
+    @Test
+    fun `split one to million`() {
+        (1L..1_000_000L).forEach { x ->
+            val vars = CoinUtils.makeSomeVariants(x)
+            assertFalse(vars.isEmpty())
+        }
+    }
+
 }