crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

CellMarkCodec.swift (2668B)


      1 import Foundation
      2 
      3 /// Single source of truth for the `(markKind, checkedRight, checkedWrong)`
      4 /// triple that the Moves wire format, `CellEntity`, and `JournalEntity` all use
      5 /// to persist a `CellMark`. The pair of booleans encodes an optional
      6 /// `CheckResult`: (false, false) = nil, (true, false) = .right,
      7 /// (false, true) = .wrong. `markKind` is 0 none / 1 pen / 2 pencil / 3 revealed.
      8 enum CellMarkCodec {
      9     static func encode(_ mark: CellMark) -> (kind: Int16, checkedRight: Bool, checkedWrong: Bool) {
     10         switch mark {
     11         case .none:
     12             return (0, false, false)
     13         case .pen(let check):
     14             return (1, check == .right, check == .wrong)
     15         case .pencil(let check):
     16             return (2, check == .right, check == .wrong)
     17         case .revealed:
     18             return (3, false, false)
     19         }
     20     }
     21 
     22     /// Inverse of `encode`. `checkedWrong` takes precedence if both somehow
     23     /// ended up true (shouldn't happen — the invariant is enforced where marks
     24     /// are constructed in `Game`, not here).
     25     static func decode(kind: Int16, checkedRight: Bool, checkedWrong: Bool) -> CellMark {
     26         let check: CheckResult?
     27         if checkedWrong {
     28             check = .wrong
     29         } else if checkedRight {
     30             check = .right
     31         } else {
     32             check = nil
     33         }
     34         switch kind {
     35         case 1: return .pen(checked: check)
     36         case 2: return .pencil(checked: check)
     37         case 3: return .revealed
     38         default: return .none
     39         }
     40     }
     41 
     42     // MARK: - Single-value encoding
     43 
     44     /// Losslessly maps the eight legal `CellMark` states to one `Int16`. Used
     45     /// by `JournalEntity`, which models the whole mark as a single field rather
     46     /// than the `markKind` + two-bool flattening the synced Moves format uses.
     47     static func code(_ mark: CellMark) -> Int16 {
     48         switch mark {
     49         case .none: return 0
     50         case .pen(nil): return 1
     51         case .pen(.right): return 2
     52         case .pen(.wrong): return 3
     53         case .pencil(nil): return 4
     54         case .pencil(.right): return 5
     55         case .pencil(.wrong): return 6
     56         case .revealed: return 7
     57         }
     58     }
     59 
     60     /// Inverse of `code(_:)`. Unknown codes decode to `.none`.
     61     static func mark(code: Int16) -> CellMark {
     62         switch code {
     63         case 1: return .pen(checked: nil)
     64         case 2: return .pen(checked: .right)
     65         case 3: return .pen(checked: .wrong)
     66         case 4: return .pencil(checked: nil)
     67         case 5: return .pencil(checked: .right)
     68         case 6: return .pencil(checked: .wrong)
     69         case 7: return .revealed
     70         default: return .none
     71         }
     72     }
     73 }