aboutsummaryrefslogtreecommitdiff
path: root/static/bv/puzzleencoder.js
blob: fcba62c5127e67f967baf4fa060ab3493f245da8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// Utility functions for compact puzzle encoding/decoding
const PuzzleEncoder = {
    // Simple substitution cipher using base64
    cipher: {
      encode(str) {
        return btoa(encodeURIComponent(str)).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
      },
      decode(str) {
        str = str.replace(/-/g, '+').replace(/_/g, '/');
        // Add back padding if needed
        while (str.length % 4) str += '=';
        try {
          return decodeURIComponent(atob(str));
        } catch (e) {
          console.error('Failed to decode:', e);
          return null;
        }
      }
    },
  
    // Compress puzzle structure by replacing common patterns
    compressPuzzle(puzzle) {
      return puzzle
        .replace(/\[\[/g, '<<')  // Replace double brackets
        .replace(/\]\]/g, '>>')  // with angle brackets
        .replace(/\[/g, '<')     // Replace single brackets
        .replace(/\]/g, '>')     // with single angle brackets
        .replace(/\s+/g, ' ');   // Normalize spaces
    },
  
    // Decompress puzzle structure
    decompressPuzzle(compressed) {
      return compressed
        .replace(/<<|〈〈/g, '[[')
        .replace(/>>|〉〉/g, ']]')
        .replace(/[<〈]/g, '[')
        .replace(/[>〉]/g, ']');
    },
  
    // Compress solutions map into compact format
    compressSolutions(solutions) {
      const pairs = Object.entries(solutions)
        .map(([expr, sol]) => `${expr}:${sol}`)
        .join('|');
      return pairs;
    },
  
    // Decompress solutions back into object
    decompressSolutions(compressed) {
      if (!compressed) return {};
      return Object.fromEntries(
        compressed.split('|')
          .map(pair => pair.split(':'))
          .filter(([k, v]) => k && v) // Filter out any invalid pairs
      );
    },
  
    // Encode entire puzzle into compact URL-safe string
    encodePuzzle(puzzleData) {
      const compressed = {
        p: this.compressPuzzle(puzzleData.initialPuzzle),
        s: this.compressSolutions(puzzleData.solutions)
      };
      return this.cipher.encode(JSON.stringify(compressed));
    },
  
    // Decode puzzle from compact string
    decodePuzzle(encoded) {
      try {
        const decoded = this.cipher.decode(encoded);
        if (!decoded) return null;
        
        const compressed = JSON.parse(decoded);
        return {
          initialPuzzle: this.decompressPuzzle(compressed.p),
          solutions: this.decompressSolutions(compressed.s)
        };
      } catch (e) {
        console.error('Failed to decode puzzle:', e);
        return null;
      }
    }
  };