From 410e2f81dea123efac3ca32e28fa7b85bb9be311 Mon Sep 17 00:00:00 2001 From: gretmn102 Date: Tue, 11 Aug 2026 02:11:12 +0300 Subject: [PATCH 01/12] =?UTF-8?q?wip:=20=D0=BD=D0=B0=D1=80=D0=B8=D1=81?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D1=81=D0=B5=D1=82=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.svelte | 30 +++++++++++------------------- src/components/Field.svelte | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 19 deletions(-) create mode 100644 src/components/Field.svelte diff --git a/src/App.svelte b/src/App.svelte index 930f655..28a8c25 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -1,24 +1,16 @@ -
-
-
- Hello world -
-
+
+
diff --git a/src/components/Field.svelte b/src/components/Field.svelte new file mode 100644 index 0000000..8c18de6 --- /dev/null +++ b/src/components/Field.svelte @@ -0,0 +1,25 @@ + + +
+ {#each new Array(colsCount * rowsCount) as _, index} +
+
i: {index}
+
({index % colsCount}, {index / colsCount | 0})
+
+ {/each} +
From a3eededb057499275aabea2469376a1133e0c562 Mon Sep 17 00:00:00 2001 From: gretmn102 Date: Tue, 11 Aug 2026 02:38:42 +0300 Subject: [PATCH 02/12] wip --- src/components/Field.svelte | 50 ++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/src/components/Field.svelte b/src/components/Field.svelte index 8c18de6..ae53db1 100644 --- a/src/components/Field.svelte +++ b/src/components/Field.svelte @@ -1,7 +1,9 @@
- {#each new Array(colsCount * rowsCount) as _, index} -
-
i: {index}
-
({index % colsCount}, {index / colsCount | 0})
-
+ {#each field.field as rows, y} + {#each rows as value, x} + + {/each} {/each}
From 33829e43852d6bf0f5515715625e0aeabc709454 Mon Sep 17 00:00:00 2001 From: gretmn102 Date: Tue, 11 Aug 2026 04:27:08 +0300 Subject: [PATCH 03/12] =?UTF-8?q?fix(Field.getIntersections):=20incorrect?= =?UTF-8?q?=20=E2=86=96,=20=E2=86=98,=20=E2=86=97,=20=E2=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/field.ts | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/lib/field.ts b/src/lib/field.ts index bbd04a7..5dee836 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -145,21 +145,25 @@ export namespace Field { } // ↖ North West - for (let y = pos.y - 1; y >= 0; y--) { + for ( + let x = pos.x - 1, y = pos.y - 1; + y >= 0 && x >= 0; + y--, x-- + ) { const rows = cols[y] - for (let x = pos.x - 1; x >= 0; x--) { - const element = rows[x] - add(element) - } + const element = rows[x] + add(element) } // ↘ South East - for (let y = pos.y + 1; y < cols.length; y++) { + for ( + let y = pos.y + 1, x = pos.x + 1; + y < cols.length && x < cols[0].length; + y++, x++ + ) { const rows = cols[y] - for (let x = pos.x + 1; x < rows.length; x++) { - const element = rows[x] - add(element) - } + const element = rows[x] + add(element) } // ↑ Upwards @@ -177,21 +181,25 @@ export namespace Field { } // ↗ North East - for (let y = pos.y - 1; y >= 0; y--) { + for ( + let y = pos.y - 1, x = pos.x + 1; + y >= 0 && x < cols[0].length; + y--, x++ + ) { const rows = cols[y] - for (let x = pos.x + 1; x < rows.length; x++) { - const element = rows[x] - add(element) - } + const element = rows[x] + add(element) } // ↙ South West - for (let y = pos.y + 1; y < cols.length; y++) { + for ( + let y = pos.y + 1, x = pos.x - 1; + y < cols.length && x >= 0; + y++, x-- + ) { const rows = cols[y] - for (let x = pos.x - 1; x >= 0; x--) { - const element = rows[x] - add(element) - } + const element = rows[x] + add(element) } // ← Leftwards From 9e5ad401351231878795347d072f315b7427997d Mon Sep 17 00:00:00 2001 From: gretmn102 Date: Tue, 11 Aug 2026 04:34:21 +0300 Subject: [PATCH 04/12] refactor(test/field/getIntersections): extract --- tests/lib/field.test.ts | 117 +++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 62 deletions(-) diff --git a/tests/lib/field.test.ts b/tests/lib/field.test.ts index 26954e0..48b4367 100644 --- a/tests/lib/field.test.ts +++ b/tests/lib/field.test.ts @@ -102,72 +102,65 @@ describe("Field.update", () => { }) }) -describe("Field.getIntersections", () => { - it("counts", () => { - const cols: (" " | "I" | "x")[][] = [ - ["I", " ", "I", " "], - [" ", " ", " ", "I"], - [" ", "I", "x", "I"], - [" ", "I", "I", "I"], - ] +type IntersectionField = (" " | "I" | "x")[][] - const field = cols.reduce( - (state, rows, y) => ( - rows.reduce( - (state, current, x) => ( - (current === "I") ? ( - immutableUpdate(state, { - field: { - $apply: field => - Field.update( - field, - { x, y }, - _ => FieldElement.create( - ElementId.create(new Date(state.id)) - ) - ) - }, - id: { - $apply: id => id + 1 - }, - }) - ) : ( - state - ) - ), - state - ) - ), { - field: Field.create(cols[0].length, cols.length), - id: 0, - } - ).field +function getIntersection(cols: IntersectionField) { + const field = cols.reduce( + (state, rows, y) => ( + rows.reduce( + (state, current, x) => ( + (current === "I") ? ( + immutableUpdate(state, { + field: { + $apply: field => Field.update( + field, + { x, y }, + _ => FieldElement.create( + ElementId.create(new Date(state.id)) + ) + ) + }, + id: { + $apply: id => id + 1 + }, + }) + ) : ( + state + ) + ), + state + ) + ), { + field: Field.create(cols[0].length, cols.length), + id: 0, + } + ).field - const targetPosition = (() => { - const result = ArrayExt.pick(cols, (rows, y) => ( - ArrayExt.pick(rows, (value, x) => { - if (value !== "x") { - return - } - return Option.mkSome({ x, y } as Position) - }) - )) - return Option2.get(result) - })() + const targetPosition = (() => { + const result = ArrayExt.pick(cols, (rows, y) => ( + ArrayExt.pick(rows, (value, x) => { + if (value !== "x") { + return; + } + return Option.mkSome({ x, y } as Position); + }) + )); + return Option2.get(result); + })() + return Field.getIntersections(field, targetPosition) +} + +describe("Field.getIntersections", () => { + it("8 counts", () => { expect( - Field.getIntersections(field, targetPosition).length + getIntersection([ + ["I", " ", "I", " "], + [" ", " ", " ", "I"], + [" ", "I", "x", "I"], + [" ", "I", "I", "I"], + ]).length ) - .toStrictEqual( - cols.reduce( - (count, rows) => rows.reduce( - (count, value) => ( - value !== "I" ? count : count + 1 - ), - count - ), - 0, - ) - ) + .toStrictEqual(8) }) }) From aa0a45528231a5ed98aabd9da6920b0293ab0ebe Mon Sep 17 00:00:00 2001 From: gretmn102 Date: Tue, 11 Aug 2026 04:36:36 +0300 Subject: [PATCH 05/12] test(field/getIntersections): add test for 4 count --- tests/lib/field.test.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/lib/field.test.ts b/tests/lib/field.test.ts index 48b4367..c225cdc 100644 --- a/tests/lib/field.test.ts +++ b/tests/lib/field.test.ts @@ -152,7 +152,7 @@ function getIntersection(cols: IntersectionField) { } describe("Field.getIntersections", () => { - it("8 counts", () => { + it("8", () => { expect( getIntersection([ ["I", " ", "I", " "], @@ -163,4 +163,15 @@ describe("Field.getIntersections", () => { ) .toStrictEqual(8) }) + it("4", () => { + expect( + getIntersection([ + ["I", " ", "I", " "], + ["x", " ", " ", "I"], + [" ", "I", " ", "I"], + [" ", "I", "I", "I"], + ]).length + ) + .toStrictEqual(4) + }) }) From 69cc7d6bbd18c10513cf5086e66ae62f469e7edb Mon Sep 17 00:00:00 2001 From: gretmn102 Date: Tue, 11 Aug 2026 04:37:44 +0300 Subject: [PATCH 06/12] wip --- src/components/Field.svelte | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/Field.svelte b/src/components/Field.svelte index ae53db1..f2765e2 100644 --- a/src/components/Field.svelte +++ b/src/components/Field.svelte @@ -1,8 +1,8 @@ @@ -11,7 +11,6 @@ "grid", "size-full", "aspect-square", - "gap-1", ])} style={`grid-template-columns: repeat(${colsCount}, minmax(0, 1fr))`} > @@ -21,7 +20,8 @@ class={concat([ "bg-gray-500", "aspect-square", - "grid-flow-row", + "border", + "border-gray-500", value ? ( "bg-white-900" ) : ( @@ -49,8 +49,8 @@ ) }} > -
{x}, {y}
-
{Field.getIntersections(field, { x, y })}
+ + {/each} {/each} From 431615b0d391830bcf50eb78e7b1042f09c95872 Mon Sep 17 00:00:00 2001 From: gretmn102 Date: Tue, 11 Aug 2026 04:55:38 +0300 Subject: [PATCH 07/12] wip --- src/components/Field.svelte | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/components/Field.svelte b/src/components/Field.svelte index f2765e2..8e9d309 100644 --- a/src/components/Field.svelte +++ b/src/components/Field.svelte @@ -22,20 +22,22 @@ "aspect-square", "border", "border-gray-500", - value ? ( - "bg-white-900" - ) : ( - Field.getIntersections(field, { x, y }).length === 0 ? ( - "bg-yellow-100" + (() => { + const intersects = Field.getIntersections(field, { x, y }).length + return value ? ( + "bg-white-900" ) : ( - Field.getIntersections(field, { x, y }).length === 1 ? ( - "bg-blue-500" + intersects === 0 ? ( + "bg-yellow-100" ) : ( - "bg-red-500" + intersects === 1 ? ( + "bg-blue-400" + ) : ( + "bg-red-500" + ) ) ) - ), - + })() ])} on:click={_ => { field = Field.update( From 2d2bae2bb53ef32b58ebc3f792459fbc7d937c57 Mon Sep 17 00:00:00 2001 From: gretmn102 Date: Tue, 11 Aug 2026 05:43:15 +0300 Subject: [PATCH 08/12] feat(Field.getIntersections): count points of intersection between two points on line --- src/lib/field.ts | 182 +++++++++++++++++++++++++++------------- tests/lib/field.test.ts | 60 +++++++++---- 2 files changed, 166 insertions(+), 76 deletions(-) diff --git a/src/lib/field.ts b/src/lib/field.ts index 5dee836..9b83c0a 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -49,6 +49,27 @@ export type Position = { y: number, } +export type IntersectionPair = [ElementId, ElementId] + +export type Intersections = { + horizontal: Option + vertical: Option + leftTopRightBottom: Option + rightTopLeftBottom: Option +} + +export namespace Intersections { + export function count(intersects: Intersections): number { + const f = (pair: Option) => ( + Option.isSome(pair) ? 1 : 0 + ) + return f(intersects.horizontal) + + f(intersects.vertical) + + f(intersects.leftTopRightBottom) + + f(intersects.rightTopLeftBottom) + } +} + export type Field = { elements: Map field: FieldElement[][] @@ -134,92 +155,135 @@ export namespace Field { export function getIntersections( field: Field, pos: Position, - ): ElementId[] { - const intersects = new Array() + ): Intersections { const cols = field.field - function add(element: FieldElement) { - if (element === undefined) { - return + + function combine( + fn1: () => Option, + fn2: () => Option + ): Option { + const result1 = fn1() + if (result1 === undefined) { + return Option.mkNone() + } + const result2 = fn2() + if (result2 === undefined) { + return Option.mkNone() } - intersects.push(element) + return Option.mkSome([result1, result2]) } - // ↖ North West - for ( - let x = pos.x - 1, y = pos.y - 1; - y >= 0 && x >= 0; - y--, x-- - ) { - const rows = cols[y] - const element = rows[x] - add(element) + /** ↖ North West */ + function leftUp() { + for ( + let x = pos.x - 1, y = pos.y - 1; + y >= 0 && x >= 0; + y--, x-- + ) { + const element = cols[y][x] + if (element === undefined) { + continue + } + return element + } } - // ↘ South East - for ( - let y = pos.y + 1, x = pos.x + 1; - y < cols.length && x < cols[0].length; - y++, x++ - ) { - const rows = cols[y] - const element = rows[x] - add(element) + /** ↘ South East */ + function rightDown() { + for ( + let y = pos.y + 1, x = pos.x + 1; + y < cols.length && x < cols[0].length; + y++, x++ + ) { + const element = cols[y][x] + if (element === undefined) { + continue + } + return element + } } - // ↑ Upwards - for (let y = pos.y - 1; y >= 0; y--) { - const rows = cols[y] - const element = rows[pos.x] - add(element) + /** ↑ Upwards */ + function up() { + for (let y = pos.y - 1; y >= 0; y--) { + const element = cols[y][pos.x] + if (element === undefined) { + continue + } + return element + } } - // ↓ Downwards - for (let y = pos.y + 1; y < cols.length; y++) { - const rows = cols[y] - const element = rows[pos.x] - add(element) + /** ↓ Downwards */ + function down() { + for (let y = pos.y + 1; y < cols.length; y++) { + const element = cols[y][pos.x] + if (element === undefined) { + continue + } + return element + } } - // ↗ North East - for ( - let y = pos.y - 1, x = pos.x + 1; - y >= 0 && x < cols[0].length; - y--, x++ - ) { - const rows = cols[y] - const element = rows[x] - add(element) + /** ↗ North East */ + function rightUp() { + for ( + let y = pos.y - 1, x = pos.x + 1; + y >= 0 && x < cols[0].length; + y--, x++ + ) { + const element = cols[y][x] + if (element === undefined) { + continue + } + return element + } } - // ↙ South West - for ( - let y = pos.y + 1, x = pos.x - 1; - y < cols.length && x >= 0; - y++, x-- - ) { - const rows = cols[y] - const element = rows[x] - add(element) + /** ↙ South West */ + function leftDown() { + for ( + let y = pos.y + 1, x = pos.x - 1; + y < cols.length && x >= 0; + y++, x-- + ) { + const element = cols[y][x] + if (element === undefined) { + continue + } + return element + } } - // ← Leftwards - { + /** ← Leftwards */ + function left() { const rows = cols[pos.y] for (let x = pos.x - 1; x >= 0; x--) { const element = rows[x] - add(element) + if (element === undefined) { + continue + } + return element } } - // → Rightwards - { + /** → Rightwards */ + function right() { const rows = cols[pos.y] for (let x = pos.x + 1; x < rows.length; x++) { const element = rows[x] - add(element) + if (element === undefined) { + continue + } + return element } } - return intersects + return { + leftTopRightBottom: combine(leftUp, rightDown), + vertical: combine(up, down), + horizontal: combine(left, right), + rightTopLeftBottom: combine(rightUp, leftDown), + } } } diff --git a/tests/lib/field.test.ts b/tests/lib/field.test.ts index c225cdc..837a8e7 100644 --- a/tests/lib/field.test.ts +++ b/tests/lib/field.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest" import { Option } from "@fering-org/functional-helper" import immutableUpdate from "immutability-helper"; -import { ElementId, Field, FieldElement, Option2, Position } from "../../src/lib/field" +import { ElementId, Field, FieldElement, Intersections, Option2, Position } from "../../src/lib/field" // refactor: [feat(ArrayExt): add pick function #11](https://github.com/gretmn102/functional-helper/issues/11) export namespace ArrayExt { @@ -102,21 +102,21 @@ describe("Field.update", () => { }) }) -type IntersectionField = (" " | "I" | "x")[][] +type IntersectionField = (" " | number | "x")[][] function getIntersection(cols: IntersectionField) { const field = cols.reduce( (state, rows, y) => ( rows.reduce( (state, current, x) => ( - (current === "I") ? ( + (typeof current === "number") ? ( immutableUpdate(state, { field: { $apply: field => Field.update( field, { x, y }, _ => FieldElement.create( - ElementId.create(new Date(state.id)) + ElementId.create(new Date(current)) ) ) }, @@ -155,23 +155,49 @@ describe("Field.getIntersections", () => { it("8", () => { expect( getIntersection([ - ["I", " ", "I", " "], - [" ", " ", " ", "I"], - [" ", "I", "x", "I"], - [" ", "I", "I", "I"], - ]).length + [ 1 , " ", 2 , 3 ], + [" ", " ", " ", 4 ], + [" ", 5 , "x", 6 ], + [" ", 7 , 8 , 9 ], + ]) ) - .toStrictEqual(8) + .toStrictEqual({ + horizontal: Option.mkSome(["5", "6"]), + vertical: Option.mkSome(["2", "8"]), + leftTopRightBottom: Option.mkSome(["1", "9"]), + rightTopLeftBottom: Option.mkSome(["4", "7"]), + } as Intersections) }) - it("4", () => { + it("0", () => { expect( getIntersection([ - ["I", " ", "I", " "], - ["x", " ", " ", "I"], - [" ", "I", " ", "I"], - [" ", "I", "I", "I"], - ]).length + [ 1 , " ", 2 , 3 ], + ["x", " ", " ", 4 ], + [" ", 5 , " ", 6 ], + [" ", 7 , 8 , 9 ], + ]) ) - .toStrictEqual(4) + .toStrictEqual({ + horizontal: Option.mkNone(), + vertical: Option.mkNone(), + leftTopRightBottom: Option.mkNone(), + rightTopLeftBottom: Option.mkNone(), + } as Intersections) + }) + it("1", () => { + expect( + getIntersection([ + [ 1 , "x", 2 , 3 ], + [" ", " ", " ", 4 ], + [" ", 5 , " ", 6 ], + [" ", 7 , 8 , 9 ], + ]) + ) + .toStrictEqual({ + horizontal: Option.mkSome(["1", "2"]), + vertical: Option.mkNone(), + leftTopRightBottom: Option.mkNone(), + rightTopLeftBottom: Option.mkNone(), + } as Intersections) }) }) From 533ee17bf167c1a996a674460fa5a568234d4cdf Mon Sep 17 00:00:00 2001 From: gretmn102 Date: Tue, 11 Aug 2026 05:54:04 +0300 Subject: [PATCH 09/12] wip --- src/components/Field.svelte | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/components/Field.svelte b/src/components/Field.svelte index 8e9d309..48500e0 100644 --- a/src/components/Field.svelte +++ b/src/components/Field.svelte @@ -1,6 +1,6 @@