Many improvements

hh
Laurent 5 years ago
parent d62285a16e
commit 2d9032c64e
  1. 5
      app/src/main/java/net/pokeranalytics/android/model/handhistory/Street.kt
  2. 36
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ActionList.kt
  3. 11
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ComputedAction.kt
  4. 2
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/HandStep.kt
  5. 67
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerConfiguration.kt
  6. 17
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerFragment.kt
  7. 53
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerModel.kt
  8. 50
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerView.kt
  9. 106
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/TableDrawer.kt
  10. 2
      app/src/main/res/layout/fragment_replayer.xml

@ -2,7 +2,6 @@ package net.pokeranalytics.android.model.handhistory
import android.content.Context
import android.graphics.Canvas
import io.realm.internal.Table
import net.pokeranalytics.android.R
import net.pokeranalytics.android.ui.modules.handhistory.replayer.HandStep
import net.pokeranalytics.android.ui.modules.handhistory.replayer.ReplayerConfiguration
@ -45,10 +44,6 @@ enum class Street : HandStep {
TableDrawer.drawStreet(this, configuration, canvas, context)
}
override fun undo(configuration: ReplayerConfiguration, canvas: Canvas, context: Context) {
TableDrawer.undoStreet(this, configuration, canvas, context)
}
override fun frames(
configuration: ReplayerConfiguration,
canvas: Canvas,

@ -20,6 +20,7 @@ interface ActionManager {
fun initialStack(position: Int): Double?
fun stackForNewAction(action: Action): Double?
fun stackAtStreetStart(index: Int): Double?
fun previousAction(action: ComputedAction): ComputedAction?
val heroIndex: Int?
}
@ -242,6 +243,20 @@ class ActionList(var listener: ActionListListener? = null) : ArrayList<ComputedA
}
/***
* Returns the list of position still in play before the given [index]
*/
fun activePositionIndexes(index: Int): MutableList<Int> {
val oustedPositions = this.take(index + 1)
.filter {
it.action.type == Action.Type.FOLD
}
.map { it.action.position }
val indexes = (0 until handHistory.numberOfPlayers).toMutableList()
indexes.removeAll(oustedPositions)
return indexes
}
/***
* Returns the list of position still in play before the given [index]
*/
@ -294,6 +309,13 @@ class ActionList(var listener: ActionListListener? = null) : ArrayList<ComputedA
return this.last { it.street == street }.action.index
}
/***
* Returns the last action index of the [street]
*/
fun lastActionIndexBeforeStreet(street: Street): Int {
return this.last { it.street == street }.action.index
}
/***
* Returns the list of empty actions before the action at the given [index]
*/
@ -685,8 +707,18 @@ class ActionList(var listener: ActionListListener? = null) : ArrayList<ComputedA
* Removes blinds for the list,
* used for the replayer/video export
*/
fun removeBlinds() {
this.dropLastWhile { this.firstOrNull()?.action?.type?.isBlind == true }
fun removeBlindActions() {
this.removeAll { it.action.type?.isBlind == true }
}
override fun previousAction(action: ComputedAction): ComputedAction? {
val index = this.indexOf(action)
return if (index > 0) {
this[index - 1]
} else {
null
}
}
}

@ -252,7 +252,7 @@ class ComputedAction(var manager: ActionManager,
this.action.effectiveAmount = significantAmount - committedAmount
}
private fun getStreetLastSignificantAction(): ComputedAction? {
fun getStreetLastSignificantAction(): ComputedAction? {
return this.manager.getStreetLastSignificantAction(this.street, this.action.index - 1)
}
@ -308,14 +308,15 @@ class ComputedAction(var manager: ActionManager,
override val positionIndex: Int
get() { return this.action.position }
val previousAction: ComputedAction?
get() {
return this.manager.previousAction(this)
}
override fun draw(configuration: ReplayerConfiguration, canvas: Canvas, context: Context) {
TableDrawer.drawAction(this, true, configuration, canvas, context)
}
override fun undo(configuration: ReplayerConfiguration, canvas: Canvas, context: Context) {
TableDrawer.undoAction(this, true, configuration, canvas, context)
}
override fun frames(
configuration: ReplayerConfiguration,
canvas: Canvas,

@ -11,7 +11,6 @@ interface HandStep {
fun frames(configuration: ReplayerConfiguration, canvas: Canvas, context: Context, update: () -> (Unit))
fun draw(configuration: ReplayerConfiguration, canvas: Canvas, context: Context)
fun undo(configuration: ReplayerConfiguration, canvas: Canvas, context: Context)
companion object {
@ -19,7 +18,6 @@ interface HandStep {
val actionList = ActionList()
actionList.load(handHistory)
actionList.removeBlinds()
val steps = mutableListOf<HandStep>()
Street.values().forEach { street ->

@ -2,25 +2,29 @@ package net.pokeranalytics.android.ui.modules.handhistory.replayer
import android.graphics.RectF
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.handhistory.Street
import net.pokeranalytics.android.model.realm.handhistory.HandHistory
import net.pokeranalytics.android.ui.modules.handhistory.model.ActionList
import net.pokeranalytics.android.ui.modules.handhistory.model.ComputedAction
import timber.log.Timber
import kotlin.math.max
class ReplayerConfiguration(var handHistory: HandHistory) {
var initialActions: List<ComputedAction>
var actionList: ActionList = ActionList(null)
private set
var steps: List<HandStep>
private var steps: List<HandStep>
private var lastBlindIndex: Int = 0
private var currentStep: Int = 0
init {
this.actionList.load(this.handHistory)
this.initialActions = actionList.filter { it.action.type?.isBlind == true }
this.lastBlindIndex = max(this.actionList.indexOfLast { it.action.type?.isBlind == true }, 0)
this.currentStep = this.lastBlindIndex // initialize at big blind
this.steps = HandStep.createSteps(this.handHistory)
}
@ -33,7 +37,8 @@ class ReplayerConfiguration(var handHistory: HandHistory) {
}
data class TextPoint(var x: Float, var y: Float, var fontSize: Float) {
fun toRect(): RectF {
return RectF(x - 2 * fontSize, y - fontSize, x + 2 * fontSize, y)
val wRatio = 1.5f
return RectF(x - wRatio * fontSize, y - fontSize, x + wRatio * fontSize, y)
}
}
@ -72,14 +77,13 @@ class ReplayerConfiguration(var handHistory: HandHistory) {
private var playerItemsHeight = 10f
private var playerItemsWidth = 10f
var chipRadius = 10f
private var chipRadius = 10f
private var paddingPercentage = 0.8f
private var cardsPaddingPercentage = 0.9f
private var tableHPadding = 0f
private var tableVPadding = 0f
val speed: Double = 1.0
val showVillainHands: Boolean = true
var potTextPoint = TextPoint(0f, 0f, 0f)
@ -235,7 +239,7 @@ class ReplayerConfiguration(var handHistory: HandHistory) {
val fontSize = this.playerItemsHeight / 3
this.playerNamePoints.add(TextPoint(rectCenterX, rectCenterY - bottomOffset, fontSize))
this.playerStackPoints.add(TextPoint(rectCenterX, rectCenterY + this.playerItemsHeight / 3, fontSize))
this.playerActionPoints.add(TextPoint(rectCenterX, rectCenterY + this.playerItemsHeight / 6, fontSize))
this.playerActionPoints.add(TextPoint(rectCenterX, rectCenterY + this.playerItemsHeight / 9, fontSize))
this.playerCircles.add(Circle(rectCenterX, rectCenterY - circleOffset, this.playerItemsHeight / 2))
@ -295,6 +299,51 @@ class ReplayerConfiguration(var handHistory: HandHistory) {
return this.playerCardRects[i]
}
fun lastActionBeforeStreet(street: Street): ComputedAction? {
this.steps.forEachIndexed { index, handStep ->
if (handStep == street && index > 0) {
return this.steps[index - 1] as ComputedAction
}
}
return null
}
fun next() {
if (this.steps.size > this.currentStep + 1) {
this.currentStep += 1
}
}
fun previous() {
if (this.currentStep > this.lastBlindIndex + 1) {
this.currentStep -= 1
}
}
val stepsToDraw: List<HandStep>
get() {
return this.steps.take(this.currentStep + 1)
}
val activePositions: List<Int>
get() {
val action = when (val step = this.steps[currentStep]) {
is ComputedAction -> {
step
}
is Street -> {
if (currentStep > 0) {
this.steps[currentStep - 1] as ComputedAction?
} else {
null
}
}
else -> throw PAIllegalStateException("unmanaged step: $step")
}
val index = action?.action?.index ?: 0
return this.actionList.activePositionIndexes(index)
}
}

@ -75,7 +75,7 @@ class ReplayerFragment : RealmFragment() {
}
private fun updateSpeedButtonText() {
this.speed.text = "${this.model.speedMultiplier}x"
this.speed.text = "${this.model.speedMultiplier.value}x"
}
private fun loadHand(handHistory: HandHistory) {
@ -101,10 +101,8 @@ class ReplayerFragment : RealmFragment() {
}
private fun nextAction() {
this.model.next?.let {
this.replayer.next(it)
}
this.model.nextStep()
this.replayer.refresh()
if (this.model.isPlaying) {
mainHandler.postDelayed(timerRunnable, this.model.actionDelay)
@ -112,18 +110,17 @@ class ReplayerFragment : RealmFragment() {
}
private fun previousAction() {
this.model.previous?.let {
this.replayer.previous(it)
}
this.model.previousStep()
this.replayer.refresh()
}
private fun nextHand() {
// TODO
}
private fun previousHand() {
// TODO
}
private fun changeSpeed() {

@ -4,48 +4,39 @@ import androidx.lifecycle.ViewModel
class ReplayerModel : ViewModel() {
enum class Multiplier(val value: Double) {
SLOW(0.75),
BASIC(1.0),
FAST(1.5)
}
var configuration: ReplayerConfiguration? = null
var isPlaying: Boolean = false
val actionSpeed = 1000L
private val actionSpeed = 1000L
var speedMultiplier = 1.0
var speedMultiplier: Multiplier = Multiplier.BASIC
private set
private var stepIndex: Int = 0
val previous: HandStep?
get() {
this.configuration?.steps?.let { steps ->
if (this.stepIndex > 1) {
this.stepIndex -= 1
return steps[this.stepIndex]
}
}
return null
}
val next: HandStep?
get() {
this.configuration?.steps?.let { steps ->
if (steps.size > this.stepIndex + 1) {
this.stepIndex += 1
return steps[this.stepIndex]
}
}
return null
}
fun previousStep() {
this.configuration?.previous()
}
fun nextStep() {
this.configuration?.next()
}
val actionDelay: Long
get() { return (speedMultiplier * actionSpeed).toLong() }
get() { return (speedMultiplier.value * actionSpeed).toLong() }
/***
* Rotate in cycles the speed
*/
fun changeSpeed() {
this.speedMultiplier = when (speedMultiplier) {
1.0 -> 1.5
1.5 -> 2.0
else -> 1.0
}
val multipliers = Multiplier.values()
val index = multipliers.indexOf(this.speedMultiplier)
this.speedMultiplier = multipliers[(index + 1) % multipliers.size]
}
}

@ -4,13 +4,12 @@ import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.view.View
import timber.log.Timber
private data class ReplayerAction(val step: HandStep, val draw: Boolean)
class ReplayerView(context: Context, attrs: AttributeSet) : View(context, attrs) {
private var actionsToDraw = mutableListOf<ReplayerAction>()
lateinit var configuration: ReplayerConfiguration
init {
@ -21,35 +20,44 @@ class ReplayerView(context: Context, attrs: AttributeSet) : View(context, attrs)
}
}
fun previous(handStep: HandStep) {
val action = ReplayerAction(handStep, false)
this.addAction(action)
}
fun next(handStep: HandStep) {
val action = ReplayerAction(handStep, true)
this.addAction(action)
}
private fun addAction(action: ReplayerAction) {
this.actionsToDraw.add(action)
fun refresh() {
this.invalidate()
}
// fun previous(handStep: HandStep) {
// val action = ReplayerAction(handStep, false)
// this.addAction(action)
// }
//
// fun next(handStep: HandStep) {
// val action = ReplayerAction(handStep, true)
// this.addAction(action)
// }
//
// private fun addAction(action: ReplayerAction) {
// this.actionsToDraw.add(action)
// this.invalidate()
// }
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
Timber.d("ReplayerView > onDraw")
canvas?.let {
this.actionsToDraw.dropWhile { action ->
if (action.draw) {
action.step.draw(this.configuration, canvas, context)
} else {
action.step.undo(this.configuration, canvas, context)
}
TableDrawer.initializeTable(configuration, canvas, context)
this.actionsToDraw.isNotEmpty()
// show cards
configuration.activePositions.forEach {
TableDrawer.drawCards(it, configuration, canvas, context)
}
// action
this.configuration.stepsToDraw.forEach { step ->
step.draw(this.configuration, canvas, context)
}
}
}

@ -22,7 +22,7 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
private const val playerStrokeWidth = 8f
private const val cardStrokeWidth = 8f
private val backgroundColor = R.color.green_darker
private const val backgroundColor = R.color.green_darker
private val backgroundPaint = Paint()
private val strokePaint = Paint()
private val fillPaint = Paint()
@ -58,6 +58,7 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
textPaint.color = context.getColor(R.color.white)
textPaint.textAlign = Paint.Align.CENTER
textPaint.isAntiAlias = true
textPaint.typeface = ResourcesCompat.getFont(context, R.font.roboto_bold)
cardTextPaint.color = context.getColor(R.color.black)
cardTextPaint.typeface = ResourcesCompat.getFont(context, R.font.roboto_bold)
@ -80,6 +81,7 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
Timber.d("Getting player $i setup ")
val playerSetup = hh.playerSetupForPosition(i)
// drawCards(i, config, canvas, context)
drawPlayerRectangle(i,false, config, canvas, context)
drawPositionAndStack(i, playerSetup?.stack, config, canvas, context)
@ -87,32 +89,15 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
drawDealerButton(config, canvas, context)
drawCards(i, config, canvas, context)
// val chipCircle = config.chipCircle(i)
// val chipText = config.chipText(i)
// drawChip(2000.0, chipText, chipCircle, canvas, context)
// drawStreet(Street.RIVER, config, canvas, context)
// drawPot(100.0, 200.0, config, canvas, context)
}
// val blinds = config.actionList.filter { it.action.type?.isBlind ?: false }
config.initialActions.forEach { action ->
action.action.amount?.let { amount ->
drawChip(amount, action.positionIndex, config, canvas, context)
}
val pot = config.actionList.potSizeForStreet(action.street)
val totalPot = config.actionList.totalPotSize(action.action.index + 1)
drawPot(pot, totalPot, config, canvas, context)
}
// drawAction(action, false, config, canvas, context)
fun drawCards(indexes: List<Int>, config: ReplayerConfiguration, canvas: Canvas, context: Context) {
// val hh = config.handHistory
for (i in indexes) {
drawCards(i, config, canvas, context)
}
}
private fun drawDealerButton(config: ReplayerConfiguration, canvas: Canvas, context: Context) {
@ -155,18 +140,18 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
}
private fun clearText(textPoint: ReplayerConfiguration.TextPoint, canvas: Canvas) {
this.clearZone(textPoint.toRect(), canvas)
this.clearRect(textPoint.toRect(), canvas)
}
private fun clearCircle(circle: ReplayerConfiguration.Circle, canvas: Canvas) {
this.clearZone(circle.toRect(), canvas)
this.clearRect(circle.toRect(), canvas)
}
private fun clearZone(rect: RectF, canvas: Canvas) {
private fun clearRect(rect: RectF, canvas: Canvas) {
canvas.drawRect(rect, this.backgroundPaint)
}
private fun drawCards(playerIndex: Int, config: ReplayerConfiguration, canvas: Canvas, context: Context) {
fun drawCards(playerIndex: Int, config: ReplayerConfiguration, canvas: Canvas, context: Context) {
val playerSetup = config.handHistory.playerSetupForPosition(playerIndex)
val cardRects = config.cardRects(playerIndex)
@ -188,6 +173,12 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
}
// private fun clearCards(playerIndex: Int, config: ReplayerConfiguration, canvas: Canvas, context: Context) {
// config.cardRects(playerIndex).forEach {
// clearRect(it, canvas)
// }
// }
private fun drawCard(card: Card, cardRect: RectF, config: ReplayerConfiguration, canvas: Canvas, context: Context) {
fillPaint.color = context.getColor(R.color.white)
@ -204,17 +195,42 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
}
fun drawPlayerPositionAndStack(action: ComputedAction, config: ReplayerConfiguration, canvas: Canvas, context: Context) {
drawPlayerRectangle(action.positionIndex,false, config, canvas, context)
drawPositionAndStack(action.positionIndex, action.stackAfterActing, config, canvas, context)
}
fun drawAction(action: ComputedAction, highlighted: Boolean = true, config: ReplayerConfiguration, canvas: Canvas, context: Context) {
// show that action is on the player by highlighting
drawPlayerRectangle(action.positionIndex, highlighted, config, canvas, context)
// redraw previous player rectangle
action.previousAction?.let { previousAction ->
drawPlayerPositionAndStack(previousAction, config, canvas, context)
}
// show action name : call, bet, check...
drawAction(action.positionIndex, action.action.type!!, config, canvas, context)
// show chips image + text, if applicable
// show pot updates, if applicable
when {
action.action.type?.isSignificant == true -> {
action.action.amount?.let { amount ->
drawChip(amount, action.positionIndex, config, canvas, context)
drawPot(action, config, canvas, context)
}
}
action.action.type?.isCall == true -> {
action.getStreetLastSignificantAction()?.action?.amount?.let { amount ->
drawChip(amount, action.positionIndex, config, canvas, context)
drawPot(action, config, canvas, context)
}
}
// action.action.type == Action.Type.FOLD -> {
// clearCards(action.positionIndex, config, canvas, context)
// }
}
}
@ -254,7 +270,7 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
val rect = config.stackRectForPlayer(i)
val rectRadius = (rect.bottom - rect.top) / 4
val color = if (highlighted) R.color.green_diamond_dark else R.color.green_darker
val color = if (highlighted) R.color.kaki else R.color.green_darker
fillPaint.color = context.getColor(color)
canvas.drawRoundRect(config.stackRectForPlayer(i), rectRadius, rectRadius, this.fillPaint)
strokePaint.color = context.getColor(R.color.green)
@ -286,7 +302,8 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
private fun drawAction(i: Int, type: Action.Type, config: ReplayerConfiguration, canvas: Canvas, context: Context) {
val pnPoint = config.pointForPlayerAction(i)
this.textPaint.textSize = pnPoint.fontSize
canvas.drawText(type.name, pnPoint.x, pnPoint.y, this.textPaint)
val actionName = context.getString(type.resId)
canvas.drawText(actionName, pnPoint.x, pnPoint.y, this.textPaint)
}
fun drawStreet(street: Street, config: ReplayerConfiguration, canvas: Canvas, context: Context) {
@ -296,9 +313,26 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
drawCard(cards[index], rectF, config, canvas, context)
}
// Clear all chips
val hh = config.handHistory
for (i in 0 until hh.numberOfPlayers) {
val pc = config.chipCircle(i)
clearCircle(pc, canvas)
val pct = config.chipText(i)
clearText(pct, canvas)
}
// Clear last action before street
config.lastActionBeforeStreet(street)?.let { action ->
drawPlayerPositionAndStack(action, config, canvas, context)
}
}
private fun drawPot(pot: Double, totalPot: Double, config: ReplayerConfiguration, canvas: Canvas, context: Context) {
private fun drawPot(action: ComputedAction, config: ReplayerConfiguration, canvas: Canvas, context: Context) {
val pot = config.actionList.potSizeForStreet(action.street)
val totalPot = config.actionList.totalPotSize(action.action.index + 1)
drawChip(pot, config.potTextPoint, config.potChipCircle, canvas, context)
val tpTextPoint = config.totalPotTextPoint
@ -310,14 +344,6 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
}
fun undoAction(computedAction: ComputedAction, highlighted: Boolean, configuration: ReplayerConfiguration, canvas: Canvas, context: Context) {
// TODO
}
fun undoStreet(street: Street, configuration: ReplayerConfiguration, canvas: Canvas, context: Context) {
// TODO
}
}
}

@ -85,7 +85,7 @@
<Button
android:id="@+id/speed"
style="@style/PokerAnalyticsTheme.TransparentButton"
android:layout_width="64dp"
android:layout_width="68dp"
android:layout_height="50dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"/>

Loading…
Cancel
Save