From fe174c3e627f6ec9aac553601ddf22299ebd66c7 Mon Sep 17 00:00:00 2001 From: William Johnson Date: Sat, 21 Feb 2026 19:54:50 -0500 Subject: [PATCH 1/4] bug-fix: Accepts level 1 --- bakeru/package.json | 2 +- bakeru/src-tauri/Cargo.toml | 10 +- bakeru/src-tauri/src/main.rs | 2 +- bakeru/src-tauri/src/solver.rs | 2 +- bakeru/src-tauri/tauri.conf.json | 6 +- bakeru/src/App.css | 33 ++++ bakeru/src/App.tsx | 5 +- bakeru/src/utils.ts | 280 +++++++++---------------------- 8 files changed, 124 insertions(+), 216 deletions(-) diff --git a/bakeru/package.json b/bakeru/package.json index 5031481..40ce58b 100644 --- a/bakeru/package.json +++ b/bakeru/package.json @@ -1,7 +1,7 @@ { "name": "bakeru", "private": true, - "version": "3.1.0", + "version": "3.2.1", "type": "module", "scripts": { "dev": "vite", diff --git a/bakeru/src-tauri/Cargo.toml b/bakeru/src-tauri/Cargo.toml index 3d3e7ee..0b0a6a8 100644 --- a/bakeru/src-tauri/Cargo.toml +++ b/bakeru/src-tauri/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "tauri_app" -version = "3.1.0" -description = "A Tauri App" -authors = ["William", "KVHO"] +name = "bakeru" +version = "3.2.1" +description = "A Shapeshifter Solver" +authors = ["William", "Kvho"] edition = "2021" [lib] -name = "tauri_app_lib" +name = "bakeru_lib" crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] diff --git a/bakeru/src-tauri/src/main.rs b/bakeru/src-tauri/src/main.rs index 2abccd9..0b1b796 100644 --- a/bakeru/src-tauri/src/main.rs +++ b/bakeru/src-tauri/src/main.rs @@ -2,5 +2,5 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] fn main() { - tauri_app_lib::run() + bakeru_lib::run() } diff --git a/bakeru/src-tauri/src/solver.rs b/bakeru/src-tauri/src/solver.rs index 543504b..4403fee 100644 --- a/bakeru/src-tauri/src/solver.rs +++ b/bakeru/src-tauri/src/solver.rs @@ -225,4 +225,4 @@ impl Solver { } steps } -} +} \ No newline at end of file diff --git a/bakeru/src-tauri/tauri.conf.json b/bakeru/src-tauri/tauri.conf.json index c68152b..f75b92b 100644 --- a/bakeru/src-tauri/tauri.conf.json +++ b/bakeru/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", - "productName": "bakeru", - "version": "3.1.0", + "productName": "Bakeru", + "version": "3.2.1", "identifier": "com.bakeru.app", "build": { "beforeDevCommand": "npm run dev", @@ -32,4 +32,4 @@ "icons/icon.ico" ] } -} \ No newline at end of file +} diff --git a/bakeru/src/App.css b/bakeru/src/App.css index 671532f..b3cbc8b 100644 --- a/bakeru/src/App.css +++ b/bakeru/src/App.css @@ -187,6 +187,39 @@ body { align-items: center; } +.parallel-toggle { + display: flex; + align-items: center; + gap: 6px; + cursor: pointer; + font-size: 14px; + color: #FBE9CE; +} + +.parallel-toggle input { + width: 16px; + height: 16px; + cursor: pointer; +} + +.threads-control { + display: flex; + align-items: center; + gap: 6px; + font-size: 14px; + color: #FBE9CE; +} + +.threads-control input { + width: 50px; + padding: 4px 8px; + border-radius: 4px; + border: 1px solid #FBE9CE; + background-color: #2c1810; + color: #FBE9CE; + font-size: 14px; +} + .result-container { width: 100%; max-width: 1000px; diff --git a/bakeru/src/App.tsx b/bakeru/src/App.tsx index c1d0dd0..b6e8528 100644 --- a/bakeru/src/App.tsx +++ b/bakeru/src/App.tsx @@ -39,7 +39,6 @@ function App() { function remapValue(raw: number, goalIdx: number, mt: number): number { - // Logic matching the Rust solver's interpretation return raw <= goalIdx ? goalIdx - raw : goalIdx + mt - raw; } @@ -114,7 +113,7 @@ function App() { } } catch (e: any) { console.error(e); - setStatus("Error: " + e.toString()); + setStatus(e.toString() + " (´•︵•`)..."); } finally { if (timerRef.current) { clearInterval(timerRef.current); @@ -191,7 +190,7 @@ function App() { - Version 3.2.0 + Version 3.2.1
diff --git a/bakeru/src/utils.ts b/bakeru/src/utils.ts index 2f049ab..4f3361f 100644 --- a/bakeru/src/utils.ts +++ b/bakeru/src/utils.ts @@ -1,216 +1,92 @@ - -// Helper function to extract shape ID from image URL -function extractShapeId(src: string): string { - const lastSlash = src.lastIndexOf('/'); - const underscore = src.lastIndexOf('_'); - if (lastSlash !== -1 && underscore !== -1 && underscore > lastSlash) { - return src.substring(lastSlash + 1, underscore); - } - return "unknown"; -} +const extractShapeId = (src: string) => + src.slice(src.lastIndexOf("/") + 1, src.lastIndexOf("_")) || "unknown"; export interface PuzzleData { width: number; height: number; grid: number[]; goal: number; - shapes: Array<{ id: number, points: number[] }>; + shapes: { id: number; points: number[] }[]; } export function parseHTML(html: string): PuzzleData { - const parser = new DOMParser(); - const doc = parser.parseFromString(html, "text/html"); - - // 1. Extract board dimensions from JavaScript - let gX = 0, gY = 0; - const scripts = Array.from(doc.getElementsByTagName('script')); - for (const script of scripts) { - const text = script.textContent || ""; - const matchGX = text.match(/gX\s*=\s*(\d+);/); - const matchGY = text.match(/gY\s*=\s*(\d+);/); - if (matchGX) gX = parseInt(matchGX[1]); - if (matchGY) gY = parseInt(matchGY[1]); - if (gX && gY) break; - } - - if (!gX || !gY) { - throw new Error("Could not parse board dimensions from script."); - } - - // 2. Find GOAL cycle table and extract cycle order - // "small[contains(text(),'GOAL')]" logic - // In DOM: find all , check text content - const smalls = Array.from(doc.getElementsByTagName('small')); - const goalSmall = smalls.find(el => el.textContent?.includes('GOAL')); - const goalTd = goalSmall?.closest('td'); - const goalCycleRow = goalTd?.parentElement; - - if (!goalCycleRow) { - throw new Error("Cannot find GOAL cycle information."); - } - - // Extract cycle order from goal row images - const cycleOrder: string[] = []; - const imgsInCycle = Array.from(goalCycleRow.getElementsByTagName('img')); - for (const img of imgsInCycle) { - const src = img.getAttribute('src') || ""; - if (!src.includes('arrow.gif')) { - const id = extractShapeId(src); - if (id && !cycleOrder.includes(id)) { - cycleOrder.push(id); - } - } - } - - const mappings = new Map(); - cycleOrder.forEach((shape, i) => mappings.set(shape, i)); - - if (cycleOrder.length === 0) { - throw new Error("Could not determine puzzle rank."); - } - - // 3. Extract goal shape - // "goalTd.SelectSingleNode..." - // In DOM: goalTd -> find img (not arrow) - const goalImgs = Array.from(goalTd?.getElementsByTagName('img') || []); - const goalImg = goalImgs.find(img => !img.getAttribute('src')?.includes('arrow.gif')); - - if (!goalImg) { - throw new Error("Cannot find goal image."); - } - - const goalShapeId = extractShapeId(goalImg.getAttribute('src') || ""); - if (!mappings.has(goalShapeId)) { - throw new Error(`Goal shape '${goalShapeId}' not in cycle order.`); - } - - const goalIndex = mappings.get(goalShapeId)!; - - // 4. Extract board state - // "table[@align='center' and @cellpadding='0']" - // This is tricky. Let's find table with correct number of rows? - // Or just look for the main board table. - // In Neopets, it's usually the one with the grid images. - const tables = Array.from(doc.getElementsByTagName('table')); - let boardTable: HTMLTableElement | null = null; - - for (const table of tables) { - if (table.align === 'center' && table.getAttribute('cellpadding') === '0') { - // Check if it looks like the board (gY rows) - // But note: DOM rows include all . - if (table.rows.length === gY) { - boardTable = table; - break; - } - } - } - - if (!boardTable) { - // Fallback: search for any table with gY rows and gX images per row? - for (const table of tables) { - if (table.rows.length === gY) { - const firstRow = table.rows[0]; - const imgs = firstRow.getElementsByTagName('img'); - if (imgs.length === gX) { - boardTable = table; - break; - } - } - } - } - - if (!boardTable) { - throw new Error(`Expected ${gY} rows in board but found none matching.`); - } - - const grid: number[] = []; - for (let r = 0; r < gY; r++) { - const row = boardTable.rows[r]; - const imgs = row.getElementsByTagName('img'); - if (imgs.length !== gX) { - throw new Error(`Expected ${gX} columns in row ${r} but found ${imgs.length}.`); - } - for (let c = 0; c < gX; c++) { - const shapeId = extractShapeId(imgs[c].getAttribute('src') || ""); - grid.push(mappings.get(shapeId) ?? 0); - } - } - - // 5. Parse Shapes - const shapes: Array<{ id: number, points: number[] }> = []; - - // Helper to extract points from a table - const parseShapeTable = (table: HTMLTableElement): number[] | null => { - const points: Array<{ x: number, y: number }> = []; - const rows = table.rows; - for (let r = 0; r < rows.length; r++) { - const cells = rows[r].cells; - for (let c = 0; c < cells.length; c++) { - const img = cells[c].querySelector("img[src*='square.gif']"); - if (img) { - points.push({ x: c, y: r }); - } - } - } - - if (points.length === 0) return null; - - // Normalize - const minX = Math.min(...points.map(p => p.x)); - const minY = Math.min(...points.map(p => p.y)); - - // Convert to flat indices on the main board? - // Wait, C# code: - // "Normalize to bounding box" -> YES. - // "var flatIndices = points.Select(p => (p.y - minY) * gX + (p.x - minX))" [Wait, *gX*?] - // The C# code uses `gX` (board width) for shape point flattening? - // YES. "Normalize to bounding box... (p.y - minY) * gX + (p.x - minX)" - // This means the shape points are represented as offsets in the MAIN GRID coordinate system. - // This matches `solver.rs` expectation where `pt % width` and `pt / width` are used. - // So `solver.rs` expects `y * board_width + x`. - - return points.map(p => (p.y - minY) * gX + (p.x - minX)); - }; + const doc = new DOMParser().parseFromString(html, "text/html"); + + // --- Dimensions --- + const scriptText = [...doc.scripts].map(s => s.textContent ?? "").join("\n"); + const gX = +(scriptText.match(/gX\s*=\s*(\d+)/)?.[1] ?? 0); + const gY = +(scriptText.match(/gY\s*=\s*(\d+)/)?.[1] ?? 0); + if (!gX || !gY) throw new Error("Board dimensions not found."); + + // --- Goal + Cycle --- + const goalSmall = [...doc.querySelectorAll("small")] + .find(s => s.textContent?.includes("GOAL")); + const goalTd = goalSmall?.closest("td"); + const goalRow = goalTd?.parentElement; + if (!goalTd || !goalRow) throw new Error("GOAL section not found."); + + const cycle = [...goalRow.querySelectorAll("img")] + .map(img => img.getAttribute("src") ?? "") + .filter(src => !src.includes("arrow.gif")) + .map(extractShapeId) + .filter((v, i, a) => v && a.indexOf(v) === i); + + if (!cycle.length) throw new Error("Cycle not found."); + + const map = new Map(cycle.map((v, i) => [v, i])); + const goalImg = [...goalTd.querySelectorAll("img")] + .find(img => !img.src.includes("arrow.gif")); + if (!goalImg) throw new Error("Goal image missing."); + + const goalIndex = map.get(extractShapeId(goalImg.src)); + if (goalIndex === undefined) throw new Error("Goal not in cycle."); + + // --- Board --- + const board = [...doc.querySelectorAll("table[align='center'][cellpadding='0']")] + .find(t => (t as HTMLTableElement).rows.length === gY) as HTMLTableElement; + if (!board) throw new Error("Board not found."); + + const grid = [...board.rows] + .flatMap(row => + [...row.querySelectorAll("img")] + .map(img => map.get(extractShapeId(img.src)) ?? 0) + ); - // ACTIVE SHAPE - // Find 'ACTIVE SHAPE' text (in tag usually) - // "activeHeader?.ParentNode.SelectNodes..." - // In DOM: find explicit text - const bigs = Array.from(doc.getElementsByTagName('big')); - const activeHeader = bigs.find(el => el.textContent?.includes('ACTIVE SHAPE')); - - if (activeHeader) { - // Find the active shape using XPath, which is more robust than manual DOM walking. - const xpathResult = doc.evaluate( - "//big[contains(text(),'ACTIVE SHAPE')]/parent::*/following-sibling::table[@cellpadding='15']//table[@cellpadding='0']", - doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null + // --- Shape Parsing --- + const shapes: { id: number; points: number[] }[] = []; + + const parseShapeTable = (table: HTMLTableElement) => { + const pts = [...table.rows].flatMap((row, y) => + [...row.cells] + .map((cell, x) => + cell.querySelector("img[src*='square.gif']") ? { x, y } : null + ) + .filter(Boolean) as { x: number; y: number }[] ); - for (let i = 0; i < xpathResult.snapshotLength; i++) { - const t = xpathResult.snapshotItem(i) as HTMLTableElement; - const pts = parseShapeTable(t); - if (pts) shapes.push({ id: shapes.length, points: pts }); - } - } - - // NEXT SHAPES - const nextHeader = bigs.find(el => el.textContent?.includes('NEXT SHAPES')); - if (nextHeader) { - const xpathResult = doc.evaluate( - "//big[contains(text(),'NEXT SHAPES')]/parent::*/following-sibling::table[@cellpadding='15']//td//table[@cellpadding='0']", + if (!pts.length) return; + + const minX = Math.min(...pts.map(p => p.x)); + const minY = Math.min(...pts.map(p => p.y)); + + shapes.push({ + id: shapes.length, + points: pts.map(p => (p.y - minY) * gX + (p.x - minX)) + }); + }; + + const parseSection = (label: string) => { + const result = doc.evaluate( + `//big[contains(normalize-space(.),'${label}')]/parent::*` + + `/following-sibling::table[1]` + + `/descendant-or-self::table[@cellpadding='0']`, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null ); - for (let i = 0; i < xpathResult.snapshotLength; i++) { - const t = xpathResult.snapshotItem(i) as HTMLTableElement; - const pts = parseShapeTable(t); - if (pts) shapes.push({ id: shapes.length, points: pts }); - } - } - - return { - width: gX, - height: gY, - grid, - goal: goalIndex, - shapes + for (let i = 0; i < result.snapshotLength; i++) + parseShapeTable(result.snapshotItem(i) as HTMLTableElement); }; -} + + parseSection("ACTIVE SHAPE"); + parseSection("NEXT SHAPE"); + + return { width: gX, height: gY, grid, goal: goalIndex, shapes }; +} \ No newline at end of file From ebee89f3c8ca682ebca5891c71b779614f0f670e Mon Sep 17 00:00:00 2001 From: William Johnson Date: Sat, 21 Feb 2026 20:02:11 -0500 Subject: [PATCH 2/4] Workflows + README update --- .github/workflows/linux.yml | 62 +++++++++++++ .github/workflows/release.yml | 163 ++++++++++++++++++++++++++++++++++ .github/workflows/windows.yml | 49 ++++++++++ README.md | 8 +- inputs/sample_level_1.html | 104 ++++++++++++++++++++++ 5 files changed, 380 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/linux.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/windows.yml create mode 100644 inputs/sample_level_1.html diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000..a767888 --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,62 @@ +name: Build Bakeru Linux + +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + build-linux: + runs-on: ubuntu-22.04 + strategy: + matrix: + include: + - target: x86_64-unknown-linux-gnu + arch: x64 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install frontend deps + run: npm install + working-directory: ./bakeru + + - name: Install Tauri CLI + run: cargo install tauri-cli --locked + + - name: Build Bakeru (AppImage, DEB, RPM) + run: | + npm run tauri build -- --target ${{ matrix.target }} --bundles appimage,deb,rpm + working-directory: ./bakeru + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + + - name: Upload AppImage + uses: actions/upload-artifact@v4 + with: + name: bakeru-linux-${{ matrix.arch }}-appimage + path: bakeru/src-tauri/target/${{ matrix.target }}/release/bundle/appimage/*.AppImage + + - name: Upload DEB + uses: actions/upload-artifact@v4 + with: + name: bakeru-linux-${{ matrix.arch }}-deb + path: bakeru/src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb + + - name: Upload RPM + uses: actions/upload-artifact@v4 + with: + name: bakeru-linux-${{ matrix.arch }}-rpm + path: bakeru/src-tauri/target/${{ matrix.target }}/release/bundle/rpm/*.rpm diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..762a086 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,163 @@ +name: Build and Release Bakeru + +on: + push: + tags: + - '*-release' + workflow_dispatch: + +jobs: + # Build for macOS + build-macos: + runs-on: macos-latest + strategy: + matrix: + include: + - target: aarch64-apple-darwin + arch: arm64 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install frontend deps + run: npm install + working-directory: ./bakeru + + - name: Install Tauri CLI + run: cargo install tauri-cli --locked + + - name: Build Bakeru + run: npm run tauri build -- --target ${{ matrix.target }} + working-directory: ./bakeru + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + + - name: Upload DMG + uses: actions/upload-artifact@v4 + with: + name: bakeru-macos-${{ matrix.arch }} + path: bakeru/src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg + + # Build for Linux + build-linux: + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install frontend deps + run: npm install + working-directory: ./bakeru + + - name: Install Tauri CLI + run: cargo install tauri-cli --locked + + - name: Build Bakeru (AppImage, DEB, RPM) + run: | + npm run tauri build -- --bundles appimage,deb,rpm + working-directory: ./bakeru + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + + - name: Upload AppImage + uses: actions/upload-artifact@v4 + with: + name: bakeru-linux-appimage + path: bakeru/src-tauri/target/release/bundle/appimage/*.AppImage + + - name: Upload DEB + uses: actions/upload-artifact@v4 + with: + name: bakeru-linux-deb + path: bakeru/src-tauri/target/release/bundle/deb/*.deb + + - name: Upload RPM + uses: actions/upload-artifact@v4 + with: + name: bakeru-linux-rpm + path: bakeru/src-tauri/target/release/bundle/rpm/*.rpm + + # Build for Windows + build-windows: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install frontend deps + run: npm install + working-directory: ./bakeru + + - name: Install Tauri CLI + run: cargo install tauri-cli --locked + + - name: Build Bakeru (MSI) + run: | + npm run tauri build -- --bundles msi + working-directory: ./bakeru + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + + - name: Upload MSI + uses: actions/upload-artifact@v4 + with: + name: bakeru-windows-msi + path: bakeru/src-tauri/target/release/bundle/msi/*.msi + + # Create GitHub Release + release: + needs: [build-macos, build-linux, build-windows] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Display structure of downloaded files + run: ls -R artifacts + + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + files: | + artifacts/bakeru-macos-*/**/*.dmg + artifacts/bakeru-linux-appimage/**/*.AppImage + artifacts/bakeru-linux-deb/**/*.deb + artifacts/bakeru-linux-rpm/**/*.rpm + artifacts/bakeru-windows-msi/**/*.msi + draft: false + prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000..c01d1bb --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,49 @@ +name: Build Bakeru Windows + +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + build-windows: + runs-on: windows-latest + strategy: + matrix: + include: + - target: x86_64-pc-windows-msvc + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install frontend deps + run: npm install + working-directory: ./bakeru + + - name: Install Tauri CLI + run: cargo install tauri-cli --locked + + - name: Build Bakeru (MSI) + run: | + npm run tauri build -- --target ${{ matrix.target }} --bundles msi + working-directory: ./bakeru + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + + - name: Upload MSI + uses: actions/upload-artifact@v4 + with: + name: bakeru-windows-msi + path: bakeru/src-tauri/target/${{ matrix.target }}/release/bundle/msi/*.msi diff --git a/README.md b/README.md index b5cc442..f883d44 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ ⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡇⢷⡏⠃⢠⠇⠀⠀⣀⠄⠀⠀⠀⣿⡖⠀⠀ %@= @@- #@# @@# *@@@ @@+ %@@@@@ ⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡇⢨⠇⠀⡼⢀⠔⠊⠀⠀⠀⠀⠀⠘⣯⣄⢀ %@= -@@@@@@@@ @@. -@@@@@@= ⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⡇⣼⡀⣰⣷⠁⠀⠀⠀⠀⠀⠀⠀⠀⣇⢻⣧⡄ -⠀ ⠀⠀⠀⠀⠀⠀⣀⣮⣿⣿⣿⣯⡭⢉⠟⠛⠳⢤⣄⣀⣀⣀⣀⡴⢠⠨⢻⣿ Version 3.1.0 +⠀ ⠀⠀⠀⠀⠀⠀⣀⣮⣿⣿⣿⣯⡭⢉⠟⠛⠳⢤⣄⣀⣀⣀⣀⡴⢠⠨⢻⣿ Version 3.2.1 ⠀ ⠀ ⢀⣾⣿⣿⣿⣿⢏⠓⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⢨⣿ ⠀ ⣰⣿⣿⣿⣿⣿⣿⡱⠌⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⢭⣾⠏ Tauri Application by @willnjohnson ⣰⡿⠟⠋⠛⢿⣿⣿⊊⠡⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⢀⣠⣼⡿⠋⠀ @@ -39,11 +39,6 @@ Originally, when I built this tool, it was built as a Windows-only Visual Studio > * Help quickly extract puzzle data with a "Copy HTML" button. > * Provide visual symbol replacement to match the solver's clean interface. -> [!WARNING] -> App might fail to give proper steps at **Level 1** (i.e. provides 1 step instead of 2 steps), but hey, you can solve it... It's really easy! -> -> But it works flawlessly for **Levels 2-100.** - ## Installation No need to install any dependencies. Just a simple installation: @@ -106,6 +101,7 @@ Throughout its development, this project has explored several search strategies * **ShapeShifter algorithm:** [Dr. Plank's lab writeup explanation](https://web.archive.org/web/20240418234629/https://web.eecs.utk.edu/~jplank/plank/classes/cs202/Labs/Lab9/) * **A-Star Heuristic search:** [CMU AI Course PDF](https://www.cs.cmu.edu/~cga/ai-course/astar.pdf) +* **Lights Out: Solutions Using Linear Algebra:** [Madsen Lights Out PDF](http://cau.ac.kr/~mhhgtx/courses/LinearAlgebra/references/MadsenLightsOut.pdf) * **She Who Shapes:** [Historical Shapeshifter Resource](https://shewhoshapes.wordpress.com/) --- diff --git a/inputs/sample_level_1.html b/inputs/sample_level_1.html new file mode 100644 index 0000000..cc5eba1 --- /dev/null +++ b/inputs/sample_level_1.html @@ -0,0 +1,104 @@ + + +Shapeshifter

LEVEL 1

+ + + + + +

+ + + + + + + + + + + +
+
1
+
0
+
1
+
0
+
0
+
0
+
0
+
0
+
0
+ +

+ + + + +
1
0

GOAL
1

+ + + +
ACTIVE SHAPE


NEXT SHAPE

+ + + + + + + + + + + + + + +

+ +


+ + +
\ No newline at end of file From cc39c84df1db38be084145e0dcbe79ae098dc8fd Mon Sep 17 00:00:00 2001 From: William Johnson Date: Sat, 21 Feb 2026 20:17:21 -0500 Subject: [PATCH 3/4] Fix Linux build dependencies --- .github/workflows/linux.yml | 13 +++++++++++++ .github/workflows/release.yml | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index a767888..470a77d 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -18,6 +18,19 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + libgtk-3-0 \ + libwebkit2gtk-4.1-0 \ + libappindicator3-1 \ + librsvg2-dev \ + patchelf \ + libxdo-dev \ + libssl-dev \ + pkg-config + - name: Install Rust uses: dtolnay/rust-toolchain@stable with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 762a086..5a1d4c8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,6 +56,19 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + libgtk-3-0 \ + libwebkit2gtk-4.1-0 \ + libappindicator3-1 \ + librsvg2-dev \ + patchelf \ + libxdo-dev \ + libssl-dev \ + pkg-config + - name: Install Rust uses: dtolnay/rust-toolchain@stable From 2a6dd047daeeffd45a715a0e57aad8c75c6c8ff5 Mon Sep 17 00:00:00 2001 From: William Johnson Date: Sat, 21 Feb 2026 20:46:00 -0500 Subject: [PATCH 4/4] Let's try Linux again --- .github/workflows/linux.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 470a77d..6df82eb 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -23,13 +23,24 @@ jobs: sudo apt-get update sudo apt-get install -y \ libgtk-3-0 \ + libgtk-3-dev \ libwebkit2gtk-4.1-0 \ + libwebkit2gtk-4.1-dev \ libappindicator3-1 \ librsvg2-dev \ patchelf \ libxdo-dev \ libssl-dev \ - pkg-config + pkg-config \ + build-essential \ + curl \ + wget \ + file \ + libx11-dev \ + libxcb1-dev \ + libxcb-render0-dev \ + libxcb-shape0-dev \ + libxcb-xfixes0-dev - name: Install Rust uses: dtolnay/rust-toolchain@stable