From b96539d348b364f2d3680bb4587970c92bc1dad5 Mon Sep 17 00:00:00 2001 From: Humza Shahid Date: Sat, 25 May 2024 13:21:14 +0100 Subject: [PATCH] refactor function in gap_buffer.sml to make use of two helper functions when inserting into the left/right --- gap_buffer.sml | 220 ++-- svelte_gap.txt | 3172 ------------------------------------------------ 2 files changed, 110 insertions(+), 3282 deletions(-) diff --git a/gap_buffer.sml b/gap_buffer.sml index bf8c401..20de924 100644 --- a/gap_buffer.sml +++ b/gap_buffer.sml @@ -42,6 +42,20 @@ struct , right = right } + fun joinEndOfLeft (newString, left) = + case left of + hd :: tail => + if isLessThanTarget (newString, hd) then (hd ^ newString) :: tail + else newString :: left + | [] => newString :: left + + fun joinStartOfRight (newString, right) = + case right of + hd :: tail => + if isLessThanTarget (newString, hd) then (newString ^ hd) :: tail + else newString :: right + | [] => newString :: right + fun preferInsertLeft (curIdx, newString, left, right) = case left of hd :: tail => @@ -60,19 +74,99 @@ struct | [] => consLeft (curIdx, newString, left, right)) | [] => consLeft (curIdx, newString, left, right) - fun joinEndOfLeft (newString, left) = - case left of - hd :: tail => - if isLessThanTarget (newString, hd) then (hd ^ newString) :: tail - else newString :: left - | [] => newString :: left + fun insLeftLeaf (prevIdx, idx, newString, curIdx, right, hd, tail) = + (* The requested index is either: + * - At the start of the left string + * - In the middle of the left string + * Find out which and split the middle of the string if necessary. *) + if idx = prevIdx then + (* At start of string. *) + { idx = curIdx + String.size newString + , right = right + , left = + (* These two meant to look reversed, + * with respect to newString and hd. + * + * The line + * `newString ^ hd` + * places the contents of newString before hd, + * and the line + * `hd :: newString` + * in a zipper also places newString before hd. + * + * Using `newString ^ hd` with `newString :: hd` gives + * different contents in the case of a zipper. + * *) + if isLessThanTarget (newString, hd) then (newString ^ hd) :: tail + else hd :: newString :: tail + } + else + (* In middle of string. *) + let + val length = idx - prevIdx + val sub1 = String.substring (hd, 0, length) + val sub2 = String.substring (hd, length, String.size hd - length) + in + if isThreeLessThanTarget (sub1, newString, sub2) then + { idx = curIdx + String.size newString + , left = (sub1 ^ newString ^ sub2) :: tail + , right = right + } + else if isLessThanTarget (sub1, newString) then + { idx = prevIdx + String.size sub1 + String.size newString + , left = (sub1 ^ newString) :: tail + , right = joinStartOfRight (sub2, right) + } + else if isLessThanTarget (newString, sub2) then + { idx = prevIdx + String.size sub1 + , left = joinEndOfLeft (sub1, tail) + , right = (newString ^ sub2) :: right + } + else + { idx = prevIdx + , left = tail + , right = sub1 :: newString :: sub2 :: right + } + end - fun joinStartOfRight (newString, right) = - case right of - hd :: tail => - if isLessThanTarget (newString, hd) then (newString ^ hd) :: tail - else newString :: right - | [] => newString :: right + fun insRightLeaf (nextIdx, idx, newString, curIdx, left, hd, tail) = + if idx = nextIdx then + (* At end of next string. *) + { idx = curIdx + , left = left + , right = + if isLessThanTarget (newString, hd) then (hd ^ newString) :: tail + else hd :: (joinStartOfRight (newString, tail)) + } + else + let + val length = idx - curIdx + val sub1 = String.substring (hd, 0, length) + val sub2 = String.substring (hd, length, String.size hd - length) + in + if isThreeLessThanTarget (sub1, newString, sub2) then + { idx = + curIdx + String.size sub1 + String.size newString + + String.size sub2 + , left = (sub1 ^ newString ^ sub2) :: left + , right = tail + } + else if isLessThanTarget (sub1, newString) then + { idx = curIdx + String.size sub1 + String.size newString + , left = (sub1 ^ newString) :: left + , right = joinStartOfRight (sub2, tail) + } + else if isLessThanTarget (newString, sub2) then + { idx = curIdx + String.size sub1 + , left = sub1 :: left + , right = (newString ^ sub2) :: tail + } + else + { idx = curIdx + String.size sub1 + String.size newString + , left = newString :: sub1 :: left + , right = joinStartOfRight (sub2, tail) + } + end fun ins (idx, newString, curIdx, left, right) : t = if curIdx = idx then @@ -87,70 +181,13 @@ struct let val prevIdx = curIdx - String.size hd in - if - idx < prevIdx - then + if idx < prevIdx then (* The requested index is prior to the string on the left, * so move leftward one string. *) ins (idx, newString, prevIdx, tail, joinStartOfRight (hd, right)) else - (* The requested index is either: - * - At the start of the left string - * - In the middle of the left string - * Find out which and split the middle of the string if necessary. *) if - idx = prevIdx - then - (* At start of string. *) - { idx = curIdx + String.size newString - , right = right - , left = - (* These two meant to look reversed, - * with respect to newString and hd. - * - * The line - * `newString ^ hd` - * places the contents of newString before hd, - * and the line - * `hd :: newString` - * in a zipper also places newString before hd. - * - * Using `newString ^ hd` with `newString :: hd` gives - * different contents in the case of a zipper. - * *) - if isLessThanTarget (newString, hd) then - (newString ^ hd) :: tail - else - hd :: newString :: tail - } - else - (* In middle of string. *) - let - val length = idx - prevIdx - val sub1 = String.substring (hd, 0, length) - val sub2 = String.substring - (hd, length, String.size hd - length) - in - if isThreeLessThanTarget (sub1, newString, sub2) then - { idx = curIdx + String.size newString - , left = (sub1 ^ newString ^ sub2) :: tail - , right = right - } - else if isLessThanTarget (sub1, newString) then - { idx = prevIdx + String.size sub1 + String.size newString - , left = (sub1 ^ newString) :: tail - , right = joinStartOfRight (sub2, right) - } - else if isLessThanTarget (newString, sub2) then - { idx = prevIdx + String.size sub1 - , left = sub1 :: tail - , right = (newString ^ sub2) :: right - } - else - { idx = prevIdx - , left = tail - , right = sub1 :: newString :: sub2 :: right - } - end + (* Call function to insert at the current node in the zipper. *) + insLeftLeaf (prevIdx, idx, newString, curIdx, right, hd, tail) end else (* Need to insert to the right. *) @@ -162,45 +199,8 @@ struct in if idx > nextIdx then ins (idx, newString, nextIdx, joinEndOfLeft (hd, left), tail) - else if idx = nextIdx then - (* At end of next string. *) - if isLessThanTarget (newString, hd) then - {idx = curIdx, left = left, right = (hd ^ newString) :: tail} - else - { idx = curIdx - , left = left - , right = hd :: (joinStartOfRight (newString, tail)) - } else - let - val length = idx - curIdx - val sub1 = String.substring (hd, 0, length) - val sub2 = String.substring - (hd, length, String.size hd - length) - in - if isThreeLessThanTarget (sub1, newString, sub2) then - { idx = - curIdx + String.size sub1 + String.size newString - + String.size sub2 - , left = (sub1 ^ newString ^ sub2) :: left - , right = tail - } - else if isLessThanTarget (sub1, newString) then - { idx = curIdx + String.size sub1 + String.size newString - , left = (sub1 ^ newString) :: left - , right = joinStartOfRight (sub2, tail) - } - else if isLessThanTarget (newString, sub2) then - { idx = curIdx + String.size sub1 - , left = sub1 :: left - , right = (newString ^ sub2) :: tail - } - else - { idx = curIdx + String.size sub1 + String.size newString - , left = newString :: sub1 :: left - , right = joinStartOfRight (sub2, tail) - } - end + insRightLeaf (nextIdx, idx, newString, curIdx, right, hd, tail) end fun insert (idx, newString, buffer: t) = diff --git a/svelte_gap.txt b/svelte_gap.txt index 6f4536d..e69de29 100644 --- a/svelte_gap.txt +++ b/svelte_gap.txt @@ -1,3172 +0,0 @@ - - - - {#if _magister} - - {/if} - - - -
- -
- - - {/if} - - - -
- - - - {#if internal_state === 'loading'} -

Loading game state

- {:else} - - - -
{game_config.topic}
- -

{stage_label}

-
-
{((internal_state === 'playing' || internal_state === 'paused') && current_stage) ? current_stage.duration - offset_sec : ''}
-
-
- - {#if (_magister == null || _magister == true)} - {#if internal_state == 'waiting'} - - {:else if internal_state == 'playing'} - - {:else if internal_state == 'paused'} - - {/if} - {/if} - -
- -
- - Info - -

{game_config.topic}

-

Room: {room} (leave)

- -
- {state === 'waiting' ? 'Waiting for the game to start' - : state === 'paused' ? 'GAME PAUSED' - : state === 'playing' ? 'Game in progress' - : ''} -
- {#if connection !== 'connected'} -
DISCONNECTED FROM GAME SERVER
- {:else} - {#if _active_sessions == 1} -
You are alone in the room
- {:else} -
{_active_sessions} players are in this room
- {/if} - {/if} - -
-

Game

- {#if game_config.meditate} -
- - Meditation (1 min) -
- {/if} - {#each Array(Math.max(game_config.rounds, 0)) as _, r} -
Round {r+1}: - {#each Array(Math.max(game_config.players, 0)) as _, p} - - {p+1} - {/each} -
- {/each} -
- -
- - {#if _magister == null || _magister == true} -
- Game controls - -

- {#if _magister == null} - This will effect all players. Will you borrow power? Will you steal it? - {:else} - You are master of the games. These controls are yours alone. - {/if} -

- - {#if internal_state == 'waiting'} - - {:else if internal_state == 'playing'} - - {:else if internal_state == 'paused'} - - {/if} - - {#if internal_state == 'paused' || internal_state == 'completed' } - - {/if} - - - - - - - - - - - - - - - -
- (Total game length: {roundish( - game_stages.reduce((x, s) => x + s.duration, 0) / 60 - )} minutes) -
- -
- {#if _magister == null} - -

Advanced - for large games

-

When present, the Magister Ludi (master of the games) has exclusive control of the game.

- {:else if _magister == true} - -

You are the master of the games. You have exclusive control over playing, pausing and configuring this game.

-

Do not close this browser window or you will be dethroned.

- {/if} -
-
- {:else} -

Magister Ludi is managing this game.

- {/if} - {/if} -
- - - {/if} - - - -
- - - - {#if internal_state === 'loading'} -

Loading game state

- {:else} - - -

Topic: {topic}< - div -g{round_audio}g id='topic'asd - f /> /h1> - -

Room: {room} (leave)

-let audio_works = true - -// function test_audio() { -// let a = new Audio() -// a.vo - (_magister == null || _magister == true) && {#if internal_state == 'waitin g'} - - - {/if} -// let audio_workstrue =// fals// e - -// function test_audi// o() { -// } - -$::$ // let a = -// a.v// olume = 0.1v// o - a.src = '/xlo// -metal-tone.mp3/// / 'anew A// udio() - // a.play ()/// / .t - hen ( - () => {a// ud -// co// nsole.log(// 'Audio works// does // // not work')// - io_work s = - audi//// o_w// ork// s // = falsetrue true} , - // () =>// {// - // // c// onsole// .l// og('Aud -function// f - console.log('xx// ') - console.lo// g(// setTimeout('xx')logco, 100 -0)nsoleix_audio(e) { - test_audio())test_ }] - { io - test_audio()t -est_au doe s -} not work' )log. - -$: { ) consol - audio_works = true}, - audio_wothenplayAudio= { - } - $$"$falseort let _active_sessions - -let game_completed = false // Derived from other properties -let internal_state -$: interna// l_sta// te = game_completed ? 'compl// eted' : state -$: { - if (s// tat// e !== 'playing') { - console.log('xxx') - game_compl -let round_audio1 -letcompletend_ audio21 -eted = false - } -} - - // export let state - -const ARCHETOPICS = [ - 'Truth', 'Human', 'Energy', 'Beauty', 'Beginning', 'End', 'Birth', 'Death', - 'Ego', 'Attention', 'Art', 'Empathy', 'Eutopia', 'Future', 'Game', 'Gift', - 'History', 'Cosmos', 'Time', 'Life', 'Addiction', 'Paradox', 'Shadow', 'Society' -] - -// Could make configurable. Eh. -co = []nst MEDITATION_SECONDS = 60 - -let game_stages -$:{ - - } `${meditate ? 'Meditation' : 'Game'} is about - type: '' - no_sound: true_to start`will start { - 3game_stages = [] - if (meditate) - type: 'meditate', game_sta - ges.pu - }]sh({ - label: 'Meditate', - duration: - if (meditate) game_stages.push({ - label: 'Meditate', - duration: MEDITATION_SECONDS, MEDITATION_SECONDS, - }) - for (let r = 0; r < rounds; r++) - type: 'bead', r, p type: 'bead', - r, p { - for (let p = 0; p < players; p++) { - game_stages.push({ - label: `Round ${r+1} player ${p+1}`, - duration: seconds_per_bead, - }) - } - } -} - - -const update_state = async patch => { - await fetch(`${room}/configure`, { - method: 'POST', - mode: 'same-origin', - headers: { - 'content-type': 'application/json', - }, - body: // JSON.stringify(patch) - }) -} - -const upd = (k, v) => () => update_state({[k]: v}) - -const config = k => e => { - console.log('k', k, e.data, e.value, e.target.value, e.target.type) - const raw_value = e.target.value - const value = e.target.type === 'number' ? raw_value|0 - : e.target.type =waiting== 'checkboxg' -co'Waiting for game to start', duration: Infinityinst complete type: 'complete',_state = { label: 'Game compl -const complete_stage = { const get_current_stage = (offset_ms) => { - if (state === 'waiting') return {stage: waiting_stage, offset_ms: 0} - - let offset_secondMath.round(rounds = offse /) 1000t_msoffset - for (let s = 0; s < game_stages.length; s++) { - let stage = game_stages[s] - if (stage.dursecation * 1000 > offset_ms) { - sec : offset_sec - const time = state + _clock_offset_cloooff=== 'playing' ? Date.now() - start_time - : state === 'paused' ? paused_progress - : 0pauslet * 1000return {stage, secoffset_ms} - } - offset_currentms -=time stage.duratiosecn * 1000 - } - r -+ paused_progresspauseeturn { - stage: complete_secstage, offset_ms - } -} -// label: 'Game complete', complete: true }ete// ' - if (state === 'waiting' || state{stage: === 'loading, offset_ms: 0}// '!== 'playing' && state - !== ') return waiting_// stagewaitinsecg, complete: trues// ac age}ivi? ye.target.checked - : raw_v// alue// - update_state({[k]: value}) -/sec/ } - -c// onst roundgisx_h = x => Math.roundnew_stage.type === 'complete'(xg// ame// _ * 1g00) // x_/ 100 - -// const startgame_ = () =// > {// -// consolge.log(x_x_'start!': xcurr_gamntex = null, -let _ggame_s// tatex_gagme) -// upstagedatege_current_stage.type === 'complete'game_statge({currstatnte: 'gpx_laying'}) -// } - -// legcurrame_ntt rogund_arr -// $: round_arrcurr = nntew Agrrg = null{}nullame_ay(Mathcomplete_stateconst {stage: new_stage, ne: new_offs: w_offs: current_ - // setTimeout needed to get around some weird race condition. - // There's probably better ways to structure this :/other ways to stage.type === 'complete'offset_ // setTimeout needed to get around some weird race condition.ms}oenew_stage: stag.max(rounds, 0)).fill()$: console.log('game state xxx', game_state)logconsnew_offsole - - -new_stage_const || state === 'paused'ise gsetTsetTimeout(() => tick(false))imeout(() => setT et_cur)rebar_nt_scurrtatent = (gobar_ffset - -letgw_stagee_ame_ {stw_stageae_cunrrte, ntoffsget_m - -$n: gaw_stage -let game_completed -$: { - // console.log('updating game_completed', current_stage) - game_completed = (state !== 'playing' || current_stage == null) ? false - : (current_stage.type === 'complete') -} - - -let internal_state -$: internal_state = game_completed ? ' completed' : stat -// let b// ar_width = 0 - - : statecurrent_stage.type - : current_stage.type === 'complete' ? 100 === 'waiting' ? 0currenstar -e// -$: {// - if (state// !== 'playing') {// - game_completed = fals// e/// / - }// -} -mee// __c// om/// / ple// ted = game_state =// = nul// l ? fnalse - if (p// lay_// audi && !new_stage.no_sou// ndno// _new_stage!! o) { - if (current_stage.complete) com// $: game_completed = current_stage && (current_stage.type === 'complete') -plete_audio.play() - else -let game_completed round_let bar_width = 0 -audio.play( - } ) : !!game_state.completegame_s tategame_ -$: width = state.complete ? 100 : 100 * offset_ms / (state.duration * 1000)$: widtinternal_state === 'waiting' -let config_open = false - - -$: if (_magister === true) config_open = tru - -// The first user has the config open by default. ysere -$: if (_active_ses=sio1ns_acti_magister == tr// TMaAke themagist iser box fully visible once there's a critical mass of players in the roomenouhg pl ue) -// let magister_opaque = false -$: magister_opa - - {#if _magist e r_ m a g isM oda l } - - body { - bac kgroun d-color: var(--bg- highlight); over flow: hidden; - } - - {/if} - -d -> - {#if isModal} - n put={config('meditate')} > - -> - - - - - - - - -
- (Tot al game length: {roundish( - (rounds * players * seconds_per_bead + (meditate ? MED ITATION_SECOND - {/if}S : 0)) / 60 - )} minutes) -
-
- (Tot al game length: {roundish( - game_stages.reduce((x, s) => x + s.durati on, 0) / 60 - )} minutes) -
- -

- - - -
-

Hello {name}!{JSON.stringify(state)}stringifyJSONh1> -

Visit the */Svelte tutorial to learn how to build Svelte apps.

-

- - - - - -
-
- -

Edit src/App.svelte and save to reload.

-

Page has been open for {count} seconds.

-

- - Learn Svelte - -

-
-
- - - - -
-
- -

Edit src/App.svelte and save to reload.

-

Page has been open for {count} seconds.

-

- - Learn Svelte - -

-
-