Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Match Scouting Phase System #12

Merged
merged 6 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/app.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer utilities {
/* Hide scrollbar for Chrome, Safari and Opera */
.no-scrollbar::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
.no-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
}
23 changes: 20 additions & 3 deletions src/lib/ActionInputStateMachine.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,27 @@ export class ActionInputVerifier {
private held_balloons: number = 0;
private held_totes: number = 0;

public get_held_tele(): { balloons: number; totes: number } {
return {
balloons: this.held_balloons,
totes: this.held_totes
};
}
public get_held_auto(): { bunnies: number; balloons: number; totes: number } {
return {
bunnies: this.held_bunnies,
balloons: this.held_balloons,
totes: this.held_totes
};
}

public verify_actions(action_data: AutoActionData[]) {
action_data
.reverse()
.forEach((action_data) => (action_data.ok = this.verify_new_action(action_data)));
action_data.forEach((action) => {
action.ok = this.verify_new_action(action);
this.held_totes = Math.max(this.held_totes, 0);
this.held_balloons = Math.max(this.held_balloons, 0);
this.held_bunnies = Math.max(this.held_bunnies, 0);
});
}

// Takes an action and returns if it's a legal one
Expand Down
6 changes: 3 additions & 3 deletions src/lib/components/Action.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
}: {
action_data: AutoActionData;
index: number;
shift: (index: number, change: number) => void;
shift: (index: number, change: -1 | 1) => void;
remove: (index: number) => void;
} = $props();

Expand All @@ -31,13 +31,13 @@
{/if}
<button
class="group-first:pointer-events-none group-first:opacity-30"
onclick={() => shift(index, -1)}
onclick={() => shift(index, 1)}
>
<MoveUp />
</button>
<button
class="group-last:pointer-events-none group-last:opacity-30"
onclick={() => shift(index, 1)}
onclick={() => shift(index, -1)}
>
<MoveDown />
</button>
Expand Down
33 changes: 25 additions & 8 deletions src/lib/components/Timeline.svelte
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
<script lang="ts">
import { ActionInputVerifier } from '$lib/ActionInputStateMachine.svelte';
import type { AutoActionData } from '$lib/types';
import type { AutoActionData, AutoHeldItems, TeleHeldItems } from '$lib/types';
import Action from './Action.svelte';

let {
actions = $bindable(),
held = $bindable(),
furthest_auto_index = $bindable(),
displaying = $bindable()
}: { actions: AutoActionData[]; displaying: boolean } = $props();
}: {
actions: AutoActionData[];
held: AutoHeldItems | TeleHeldItems;
furthest_auto_index: number;
displaying: boolean;
} = $props();

/// Determine if currying is the right solution or if we should use a binding
function remove(index: number) {
if (furthest_auto_index >= index) furthest_auto_index--;
actions.splice(index, 1);
verify();
}

function shift(index: number, change: number) {
let item = actions[index];
actions.splice(index, 1);
actions.splice(index + change, 0, item);
function shift(index: number, change: -1 | 1) {
//if (furthest_auto_index === index) furthest_auto_index += change;
//else if (furthest_auto_index === index + change) furthest_auto_index = index;

[actions[index], actions[index + change]] = [actions[index + change], actions[index]];
verify();
}

function verify() {
new ActionInputVerifier().verify_actions(actions);
const action_input_verifier = new ActionInputVerifier();
action_input_verifier.verify_actions(actions);
// TODO: Fix this horrible and jank solution
held = Object.hasOwn(held, 'bunnies')
? action_input_verifier.get_held_auto()
: action_input_verifier.get_held_tele();
}
const is_valid_timeline = $derived(actions.filter((action) => !action.ok).length === 0);
</script>
Expand All @@ -36,7 +50,7 @@
}}
>
<div
class="absolute inset-x-0 bottom-0 flex h-[50dvh] w-dvw flex-col items-center gap-3 rounded-t-lg bg-gunmetal p-3 text-white"
class="no-scrollbar absolute inset-x-0 bottom-0 flex h-[50dvh] w-dvw flex-col items-center gap-3 overflow-y-scroll rounded-t-lg bg-gunmetal p-3 text-white"
id="timeline"
>
{#each actions as _, i}
Expand All @@ -46,6 +60,9 @@
{remove}
{shift}
/>
{#if furthest_auto_index === actions.length - i - 1}
<hr />
azaleacolburn marked this conversation as resolved.
Show resolved Hide resolved
{/if}
{/each}
{#if actions.length === 0}
<h3 class="m-auto">No actions yet :3</h3>
Expand Down
6 changes: 6 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ export type BunnyAction =
| 'ScoreBunnyLow';
export type AutoAction = TeleAction | BunnyAction;

export type TeleHeldItems = {
balloons: number;
totes: number;
};
export type AutoHeldItems = TeleHeldItems & { bunnies: number };

// For state machine
export type ItemInputState = 'Intake' | 'Score' | 'Eject' | 'None';
export type TeleInputState = TeleAction | ItemInputState;
Expand Down
115 changes: 89 additions & 26 deletions src/routes/scout/[team_data]/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,46 +1,109 @@
<script lang="ts">
import type { TeamMatch, AutoActionData } from '$lib/types';
import type { TeamMatch, AutoActionData, AutoHeldItems, TeleActionData } from '$lib/types';
import Timeline from '$lib/components/Timeline.svelte';
import ActionInputs from './ActionInputs.svelte';
import AutoActionInputs from './AutoActionInputs.svelte';
import TeleActionInputs from './TeleActionInputs.svelte';
import type { PageData } from './$types';
import { ArrowRight, ArrowLeft } from 'lucide-svelte';
import { browser } from '$app/environment';

const { data }: { data: PageData } = $props();

const scout_id = browser ? (localStorage?.getItem('scout_id') ?? '') : '';

let actions: AutoActionData[] = $state([]);
let held: AutoHeldItems = $state({
bunnies: 0,
balloons: 0,
totes: 0
});
// The furthest index in actions that was made during auto
let furthest_auto_index = $state(0);

let skill = $state(0);
let broke = $state(false);
let died = $state(false);
let notes = $state('');

let timelineExtended = $state(false);
let gamePhase = $state('Auto') as 'Auto' | 'Tele' | 'Post';
let pageName = $state('');

const match: TeamMatch = $state({
id: 0,
scout_id,
team_key: data.team_key,
match_key: data.match_key,
skill: 3,
notes: '',
broke: false,
died: false,
auto_actions: [],
tele_actions: []
});
function phaseShiftRight() {
gamePhase = gamePhase === 'Auto' ? 'Tele' : gamePhase === 'Tele' ? 'Post' : 'Post'; // Last case should never happen
}
function phaseShiftLeft() {
gamePhase = gamePhase === 'Post' ? 'Tele' : gamePhase === 'Tele' ? 'Auto' : 'Auto'; // Last case should never happen
}

function submit() {
const auto_actions = actions.slice(0, furthest_auto_index + 1);
const tele_actions = actions.slice(furthest_auto_index + 1) as TeleActionData[]; // TODO: Add verification function to ensure that this always works
const match: TeamMatch = {
id: 0,
scout_id,
team_key: data.team_key,
match_key: data.match_key,
skill,
broke,
died,
notes,
auto_actions,
tele_actions
};
}
</script>

<div class="m-auto flex h-dvh max-w-md flex-col items-center gap-2 p-2">
<div class="flex w-full justify-between border-b-2 border-white/10 pb-2 font-semibold">
<span class="flex-grow">Team {data.team_key}</span>
<span class="flex-grow text-right">{pageName}</span>
<span class="flex-shrink-0">Team {data.team_key}</span>
<div class="flex gap-2">
<button
onclick={phaseShiftLeft}
class={gamePhase === 'Auto' ? 'pointer-events-none opacity-30' : ''}
><ArrowLeft /></button
>
<span class="text-right">{gamePhase}: {pageName}</span>
<button
onclick={phaseShiftRight}
class={gamePhase === 'Post' ? 'pointer-events-none opacity-30' : ''}
><ArrowRight /></button
>
</div>
</div>

<ActionInputs bind:actions bind:pageName />

<button
class="w-full border-t-2 border-white/10 pt-2 text-center font-semibold"
onclick={(e: Event) => {
e.stopPropagation();
timelineExtended = true;
}}>Show Timeline</button
>
<Timeline bind:actions bind:displaying={timelineExtended} />
{#if gamePhase === 'Auto'}
<AutoActionInputs bind:furthest_auto_index bind:held bind:actions bind:pageName />
<button
class="w-full border-t-2 border-white/10 pt-2 text-center font-semibold"
onclick={(e: Event) => {
e.stopPropagation();
timelineExtended = true;
}}>Show Timeline</button
>
<Timeline
bind:furthest_auto_index
bind:held
bind:actions
bind:displaying={timelineExtended}
/>
{:else if gamePhase === 'Tele'}
<TeleActionInputs bind:held bind:actions bind:pageName />
<button
class="w-full border-t-2 border-white/10 pt-2 text-center font-semibold"
onclick={(e: Event) => {
e.stopPropagation();
timelineExtended = true;
}}>Show Timeline</button
>
<Timeline
bind:furthest_auto_index
bind:held
bind:actions
bind:displaying={timelineExtended}
/>
{:else}
<div>Postmatch</div>
<button class="mt-auto w-full rounded bg-gunmetal p-2 font-bold">Submit</button>
{/if}
</div>
Loading
Loading