Skip to content

Commit

Permalink
Load hand json from current settings, on any change
Browse files Browse the repository at this point in the history
  • Loading branch information
dylan-chong committed Sep 28, 2023
1 parent e834b6a commit 045d2fe
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 64 deletions.
2 changes: 1 addition & 1 deletion src/components/BoardSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import BoardSelectorCard from "./BoardSelectorCard.vue";
const config = useConfigStore();
const boardText = ref("");
watch(() => config.board, (_newBoard) => setBoardTextFromButtons(), { deep: true });
watch(() => config.board, () => setBoardTextFromButtons(), { deep: true });
const toggleCard = (cardId: number) => {
if (config.board.includes(cardId)) {
Expand Down
110 changes: 56 additions & 54 deletions src/components/HandImporter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="mt-1 w-[52rem]">
<textarea
v-model="importText"
class="block w-full h-[32rem] px-2 py-1 rounded-lg textarea text-sm font-mono"
class="block w-full h-[36rem] px-2 py-1 rounded-lg textarea text-sm font-mono"
@change="onImportTextChanged"
spellcheck="false"
/>
Expand All @@ -22,60 +22,61 @@
</template>

<script setup lang="ts">
import { ref } from "vue";
import { useConfigStore, useStore } from "../store";
import { parseCardString, Validation } from "../utils";
import { ref, watch } from "vue";
import { Config, configKeys, useConfigStore, useStore } from "../store";
import { cardText, parseCardString, Position, Validation } from "../utils";
import { setRange, validateRange } from "../range-utils";
import * as invokes from "../invokes";
type Config = Record<string, any>;
type NonValidatedHandJSON = Record<string, any>;
type HandJSON = {
oopRange: string,
ipRange: string,
config: Omit<Config, 'board'> & { board: string[] }
}
const INVALID_BOARD_ERROR = `Invalid '.config.board'. Set board cards manually for an example.`;
const config = useConfigStore();
const store = useStore();
const importText = ref("{\n \n}");
const importTextError = ref("");
const importDoneText = ref("");
// TODO load this stuff from existing stuff on page enter
const importText = ref(`{
"oopRange": "A9s,A8s,A7s,A6s,A5s,A4s,A3s,A2s,KTs,K9s,K8s,K7s,K6s,K5s,K4s,K3s,K2s,KQo,QTs,Q9s,Q8s,Q7s,Q6s,Q5s,AJo,KJo,QJo,J9s,J8s,J7s,ATo,KTo,QTo,JTo,T9s,T8s,T7s,A9o,98s,97s,96s,A8o,88,87s,86s,85s,77,66,A5o,55,44,33,22,ATs:0.5,KJs:0.5,AQo:0.5,QJs:0.5,JTs:0.5,99:0.5,76s:0.5,75s:0.5,74s:0.5,65s:0.5,64s:0.5,54s:0.5,53s:0.5,43s:0.5",
"ipRange": "AA,AKs,AQs,AJs,ATs,A9s,A8s,A7s,A6s,A5s,A4s,A3s,A2s,AKo,KK,KQs,KJs,KTs,K9s,K8s,K7s,K6s,AQo,KQo,QQ,QJs,QTs,Q9s,Q8s,AJo,KJo,QJo,JJ,JTs,J9s,J8s,ATo,KTo,QTo,JTo,TT,T9s,T8s,99,98s,97s,88,87s,86s,77,76s,75s,66,65s,64s,55,54s,44,33,22,K5s:0.5,K4s:0.5,K3s:0.5,K2s:0.5,Q7s:0.5,Q6s:0.5,Q5s:0.5,Q4s:0.5,J7s:0.5,J6s:0.5,T7s:0.5,T6s:0.5,A9o:0.5,K9o:0.5,Q9o:0.5,J9o:0.5,T9o:0.5,96s:0.5,95s:0.5,A8o:0.5,98o:0.5,85s:0.5,84s:0.5,74s:0.5,63s:0.5,A5o:0.5,53s:0.5,43s:0.5,32s:0.5",
"config": {
"board": ["Ah", "Qc", "2d"],
"startingPot": 45,
"effectiveStack": 123,
"rakePercent": 0,
"rakeCap": 0,
"donkOption": false,
"oopFlopBet": "50, 75",
"oopFlopRaise": "60",
"oopTurnBet": "50, 75",
"oopTurnRaise": "60",
"oopTurnDonk": "",
"oopRiverBet": "50, 75",
"oopRiverRaise": "60",
"oopRiverDonk": "",
"ipFlopBet": "50, 75",
"ipFlopRaise": "60",
"ipTurnBet": "50, 75",
"ipTurnRaise": "60",
"ipRiverBet": "50, 75",
"ipRiverRaise": "60",
"addAllInThreshold": 150,
"forceAllInThreshold": 20,
"mergingThreshold": 10,
"addedLines": "",
"removedLines": ""
}
}`);
watch(() => store.ranges, () => generateImportText(), { deep: true });
watch(() => Object.values(config), () => generateImportText(), { deep: true });
const generateImportText = async () => {
const configObj = Object.fromEntries(
Object.entries(config).filter(([key, _value]) => configKeys.indexOf(key) !== -1)
);
const importObj = {
oopRange: await invokes.rangeToString(Position.OOP),
ipRange: await invokes.rangeToString(Position.IP),
config: {
...configObj,
board: config.board.map((cardId) => {
const { rank, suitLetter } = cardText(cardId);
return rank + suitLetter;
})
}
};
importText.value = JSON.stringify(importObj, null, 2);
};
const onImportTextChanged = () => {
importDoneText.value = '';
validateImportTextAndDisplayError();
}
const validateConfigPrimitives = (importConfig: Config): Validation => {
for (const key in importConfig) {
const validateConfigPrimitives = (importConfig: any): Validation => {
if (typeof importConfig !== 'object')
return { success: false, error: `Expected '.config' to be an object but got ${typeof importConfig}` };
for (const key of configKeys) {
const newValue = importConfig[key];
const existingValue = (config as any)[key];
if (existingValue === null || existingValue === undefined) continue;
Expand Down Expand Up @@ -112,7 +113,7 @@ const validateBoard = (board: any): Validation => {
return { success: true };
};
const parseJson = (json: string): Validation & { json?: Config } => {
const parseJson = (json: string): Validation<{ json?: NonValidatedHandJSON }> => {
try {
return { success: true, json: JSON.parse(json) };
} catch (e) {
Expand All @@ -121,7 +122,7 @@ const parseJson = (json: string): Validation & { json?: Config } => {
}
}
const validateImportTextAndDisplayError = (): Validation & { json?: Config } => {
const validateImportTextAndDisplayError = (): Validation<{ json?: HandJSON }> => {
importTextError.value = '';
const validation = validateImportText();
Expand All @@ -131,20 +132,19 @@ const validateImportTextAndDisplayError = (): Validation & { json?: Config } =>
return validation;
}
const validateImportText = (): Validation & { json?: Config } => {
const validateImportText = (): Validation<{ json?: HandJSON }> => {
const parseValidation = parseJson(importText.value);
if (!parseValidation.success) return parseValidation;
const importJson = parseValidation.json as Config;
const importJson = parseValidation.json;
if (typeof importJson !== 'object')
return { success: false, error: 'Not a valid JSON object' };
const validateFns: (() => Validation)[] = [
// TODO these validations should accept any input and validate
() => validateConfigPrimitives(config),
() => validateBoard(importJson?.config?.board),
() => validateRange(importJson.oopRange),
() => validateRange(importJson.ipRange),
() => validateBoard(importJson.config?.board),
() => validateRange(importJson.oopRange, 'oopRange'),
() => validateRange(importJson.ipRange, 'ipRange'),
]
for (const validate of validateFns) {
const validation = validate();
Expand All @@ -153,22 +153,24 @@ const validateImportText = (): Validation & { json?: Config } => {
importJson.config.board = importJson.config.board.map(parseCardString);
return { success: true, json: importJson };
return { success: true, json: importJson as HandJSON };
};
const importHand = async () => {
const { success, json } = validateImportTextAndDisplayError();
if (!success) return;
const importJson = json as Config;
const validation = validateImportTextAndDisplayError();
if (!validation.success) return;
const importJson = validation.json as HandJSON;
for (let key in importJson.config) {
const newValue = importJson.config[key];
const newValue = (importJson.config as Record<string, any>)[key];
(config as any)[key] = newValue;
}
await setRange(0, importJson.oopRange, store);
await setRange(1, importJson.ipRange, store);
await setRange(Position.OOP, importJson.oopRange, store);
await setRange(Position.IP, importJson.ipRange, store);
importDoneText.value = 'Done!';
};
generateImportText();
</script>
4 changes: 2 additions & 2 deletions src/components/RangeEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import { useStore } from "../store";
import { ranks } from "../utils";
import { Position, ranks } from "../utils";
import { setRange, validateRange } from "../range-utils";
import * as invokes from "../invokes";
Expand All @@ -148,7 +148,7 @@ const yellow500 = "#eab308";
type DraggingMode = "none" | "enabling" | "disabling";
const props = withDefaults(
defineProps<{ player: number; defaultText?: string }>(),
defineProps<{ player: Position; defaultText?: string }>(),
{ defaultText: "" }
);
Expand Down
6 changes: 3 additions & 3 deletions src/components/SideBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
>
OOP Range
<span class="flex my-2 justify-center">
<RangeMiniViewer :player="0" />
<RangeMiniViewer :player="Position.OOP" />
</span>
</button>

<button :class="itemStyle('ip-range')" @click="store.sideView = 'ip-range'">
IP Range
<span class="flex my-2 justify-center">
<RangeMiniViewer :player="1" />
<RangeMiniViewer :player="Position.IP" />
</span>
</button>

Expand Down Expand Up @@ -66,7 +66,7 @@
<script setup lang="ts">
import { computed } from "vue";
import { SideView, useStore, useConfigStore } from "../store";
import { cardText } from "../utils";
import { Position, cardText } from "../utils";
import RangeMiniViewer from "./RangeMiniViewer.vue";
Expand Down
9 changes: 6 additions & 3 deletions src/range-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Validation } from "./utils";
import { Position, Validation } from "./utils";
import * as invokes from "./invokes";
import { useStore } from "./store";

Expand All @@ -15,7 +15,10 @@ export const rangeRegex = new RegExp(
const trimRange = (rangeString: string) =>
rangeString.replace(trimRegex, "$1").trim();

export const validateRange = (rangeString: string): Validation => {
export const validateRange = (rangeString: any, keyForError: string = 'range'): Validation => {
if (typeof rangeString !== 'string')
return { success: false, error: `Expected '${keyForError}' to be a string but got '${typeof rangeString}` };

const trimmed = trimRange(rangeString)
const ranges = trimmed.split(",");

Expand All @@ -30,7 +33,7 @@ export const validateRange = (rangeString: string): Validation => {
return { success: true };
};

export const setRange = async (player: number, rangeString: string, store: Store): Promise<Validation> => {
export const setRange = async (player: Position, rangeString: string, store: Store): Promise<Validation> => {
const trimmed = rangeString.replace(trimRegex, "$1").trim();
const validation = validateRange(rangeString);
if (!validation.success) return validation;
Expand Down
60 changes: 59 additions & 1 deletion src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,64 @@ export type SideView =
| "run-solver"
| "about";

export type Config = {
board: number[],
startingPot: number,
effectiveStack: number,
rakePercent: number
rakeCap: number
donkOption: boolean,
oopFlopBet: string,
oopFlopRaise: string,
oopTurnBet: string,
oopTurnRaise: string,
oopTurnDonk: string,
oopRiverBet: string,
oopRiverRaise: string,
oopRiverDonk: string,
ipFlopBet: string,
ipFlopRaise: string,
ipTurnBet: string,
ipTurnRaise: string,
ipRiverBet: string,
ipRiverRaise: string,
addAllInThreshold: number,
forceAllInThreshold: number,
mergingThreshold: number,
expectedBoardLength: number
addedLines: string,
removedLines: string,
};

export const configKeys = [
"board",
"startingPot",
"effectiveStack",
"rakePercent",
"rakeCap",
"donkOption",
"oopFlopBet",
"oopFlopRaise",
"oopTurnBet",
"oopTurnRaise",
"oopTurnDonk",
"oopRiverBet",
"oopRiverRaise",
"oopRiverDonk",
"ipFlopBet",
"ipFlopRaise",
"ipTurnBet",
"ipTurnRaise",
"ipRiverBet",
"ipRiverRaise",
"addAllInThreshold",
"forceAllInThreshold",
"mergingThreshold",
"expectedBoardLength",
"addedLines",
"removedLines",
];

export const saveConfigTmp = () => {
const config = useConfigStore();
const tmpConfig = useTmpConfigStore();
Expand Down Expand Up @@ -122,7 +180,7 @@ export const useStore = defineStore("app", {
});

export const useConfigStore = defineStore("config", {
state: () => ({
state: (): Config => ({
board: [] as number[],
startingPot: 20,
effectiveStack: 100,
Expand Down
6 changes: 6 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export const ranks = [
export const suits = ["♣", "♦", "♥", "♠"];
export const suitLetters = ["c", "d", "h", "s"];

/** TODO use this everywhere instead of num */
export enum Position {
OOP = 0,
IP = 1
}

const suitClasses = [
"text-green-600",
"text-blue-600",
Expand Down

0 comments on commit 045d2fe

Please sign in to comment.