Skip to content

Commit e4200b1

Browse files
committed
refactoring: move RUL2-loading to Rul2Model
1 parent 10acc35 commit e4200b1

2 files changed

Lines changed: 67 additions & 56 deletions

File tree

src/main/scala/module/Rul2Model.scala

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.sc4nam.module
22

33
import java.nio.file.{Files, Paths, Path}
4-
import io.github.memo33.metarules.meta.{RotFlip, Rule, IdTile}
4+
import io.github.memo33.metarules.meta.{RotFlip, EquivRule, Rule, IdTile}
55
import RotFlip._
66
import syntax.IdTile
77

@@ -77,4 +77,57 @@ object Rul2Model {
7777
else None
7878
}
7979

80+
def load(directory: Path): Rul2Model = {
81+
val rulesRhd = collection.mutable.Map.empty[EquivRule, Rule[IdTile]]
82+
val rulesLhd = collection.mutable.Map.empty[EquivRule, Rule[IdTile]]
83+
val rulesShared = collection.mutable.Map.empty[EquivRule, Rule[IdTile]]
84+
val lookupRuleRhd: PartialFunction[EquivRule, Rule[IdTile]] = rulesShared.orElse(rulesRhd) // the two maps should be disjoint
85+
val lookupRuleLhd: PartialFunction[EquivRule, Rule[IdTile]] = rulesShared.orElse(rulesLhd) // the two maps should be disjoint
86+
87+
LOGGER.info(s"""Loading all RUL2 code for RHD and LHD from "$directory"""")
88+
iterateRulFiles(directory).foreach { path =>
89+
val drivesideFile = drivesideOfFile(path)
90+
scala.util.Using.resource(new java.util.Scanner(path.toFile(), "UTF-8")) { scanner =>
91+
while(scanner.hasNextLine()) {
92+
parseRuleWithRestrictedDriveside(scanner.nextLine(), drivesideFile) match {
93+
// only the first matching rule is loaded by the game, so we store only the first one read
94+
case Some((rule, Rhd)) =>
95+
val key = new EquivRule(rule)
96+
if (!rulesShared.contains(key) && !rulesRhd.contains(key))
97+
rulesRhd.addOne(key, rule)
98+
case Some((rule, Lhd)) =>
99+
val key = new EquivRule(rule)
100+
if (!rulesShared.contains(key) && !rulesLhd.contains(key))
101+
rulesLhd.addOne(key, rule)
102+
case Some((rule, RhdAndLhd)) =>
103+
val key = new EquivRule(rule)
104+
if (!rulesShared.contains(key)) {
105+
if (!rulesRhd.contains(key) && !rulesLhd.contains(key))
106+
rulesShared.addOne(key, rule)
107+
else
108+
require(
109+
rulesRhd.contains(key) == rulesLhd.contains(key),
110+
s"There's an unexpected rule conflict that differs between RHD and LHD: $rule"
111+
)
112+
}
113+
case None => // ignore
114+
}
115+
}
116+
}
117+
}
118+
119+
new Rul2Model(lookupRuleRhd, lookupRuleLhd)
120+
}
121+
122+
def evaluateRulesOnce(lookupRule: PartialFunction[EquivRule, Rule[IdTile]], t0: IdTile, t1: IdTile): Option[(IdTile, IdTile)] = {
123+
val key = new EquivRule(Rule(t0, t1, t0, t1))
124+
lookupRule.unapply(key).flatMap(rule => applyRule(rule, t0, t1))
125+
}
126+
80127
}
128+
129+
/** Holds all RUL2 code in memory. Instantiate this with `Rul2Model.load`. */
130+
class Rul2Model private (
131+
val lookupRuleRhd: PartialFunction[EquivRule, Rule[IdTile]],
132+
val lookupRuleLhd: PartialFunction[EquivRule, Rule[IdTile]],
133+
)

src/main/scala/scripts/RedundantAdjacenciesChecker.scala

Lines changed: 13 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -26,49 +26,12 @@ object RedundantAdjacenciesChecker {
2626
* redundant code.
2727
*/
2828
def main(args: Array[String]): Unit = {
29-
checkRedundantAdjacencies()
29+
val rul2 = Rul2Model.load(Paths.get("Controller/RUL2"))
30+
checkRedundantAdjacencies(rul2)
3031
}
3132

32-
def checkRedundantAdjacencies(): Unit = {
33-
val rulesRhd = collection.mutable.Map.empty[EquivRule, Rule[IdTile]]
34-
val rulesLhd = collection.mutable.Map.empty[EquivRule, Rule[IdTile]]
35-
val rulesShared = collection.mutable.Map.empty[EquivRule, Rule[IdTile]]
36-
val lookupRuleRhd: PartialFunction[EquivRule, Rule[IdTile]] = rulesShared.orElse(rulesRhd) // the two maps should be disjoint
37-
val lookupRuleLhd: PartialFunction[EquivRule, Rule[IdTile]] = rulesShared.orElse(rulesLhd) // the two maps should be disjoint
38-
39-
LOGGER.info("caching all RUL2 code for RHD and LHD")
40-
iterateRulFiles(Paths.get("Controller/RUL2")).foreach { path =>
41-
val drivesideFile = drivesideOfFile(path)
42-
scala.util.Using.resource(new java.util.Scanner(path.toFile(), "UTF-8")) { scanner =>
43-
while(scanner.hasNextLine()) {
44-
parseRuleWithRestrictedDriveside(scanner.nextLine(), drivesideFile) match {
45-
// only the first matching rule is loaded by the game, so we store only the first one read
46-
case Some((rule, Rhd)) =>
47-
val key = new EquivRule(rule)
48-
if (!rulesShared.contains(key) && !rulesRhd.contains(key))
49-
rulesRhd.addOne(key, rule)
50-
case Some((rule, Lhd)) =>
51-
val key = new EquivRule(rule)
52-
if (!rulesShared.contains(key) && !rulesLhd.contains(key))
53-
rulesLhd.addOne(key, rule)
54-
case Some((rule, RhdAndLhd)) =>
55-
val key = new EquivRule(rule)
56-
if (!rulesShared.contains(key)) {
57-
if (!rulesRhd.contains(key) && !rulesLhd.contains(key))
58-
rulesShared.addOne(key, rule)
59-
else
60-
require(
61-
rulesRhd.contains(key) == rulesLhd.contains(key),
62-
s"There's an unexpected rule conflict that differs between RHD and LHD: $rule"
63-
)
64-
}
65-
case None => // ignore
66-
}
67-
}
68-
}
69-
}
70-
71-
LOGGER.info("searching for redundant adjacencies in RUL2 code")
33+
def checkRedundantAdjacencies(rul2: Rul2Model): Unit = {
34+
LOGGER.info("Searching for redundant adjacencies in RUL2 code")
7235
iterateRulFiles(Paths.get("Controller/RUL2")).foreach { path =>
7336
val drivesideFile = drivesideOfFile(path)
7437
val tmpPath = path.resolveSibling(path.getFileName().toString() + ".tmp")
@@ -82,12 +45,12 @@ object RedundantAdjacenciesChecker {
8245

8346
val redundant =
8447
parseRuleWithRestrictedDriveside(line, drivesideFile) match {
85-
case Some((rule, Rhd)) => isRedundantAdjacency(rule, lookupRuleRhd)
86-
case Some((rule, Lhd)) => isRedundantAdjacency(rule, lookupRuleLhd)
48+
case Some((rule, Rhd)) => isRedundantAdjacency(rule, rul2.lookupRuleRhd)
49+
case Some((rule, Lhd)) => isRedundantAdjacency(rule, rul2.lookupRuleLhd)
8750
case Some((rule, RhdAndLhd)) =>
88-
val b = isRedundantAdjacency(rule, lookupRuleRhd)
51+
val b = isRedundantAdjacency(rule, rul2.lookupRuleRhd)
8952
require(
90-
b == isRedundantAdjacency(rule, lookupRuleLhd),
53+
b == isRedundantAdjacency(rule, rul2.lookupRuleLhd),
9154
s"Redundancies should be the same for RHD and LHD: $rule" // hopefully this will always be the case
9255
)
9356
b
@@ -133,19 +96,14 @@ object RedundantAdjacenciesChecker {
13396
(a * rot, b * rot, southBound)
13497
}}
13598

136-
def evaluateRulesOnce(lookupRule: PartialFunction[EquivRule, Rule[IdTile]], t0: IdTile, t1: IdTile): Option[(IdTile, IdTile)] = {
137-
val key = new EquivRule(Rule(t0, t1, t0, t1))
138-
lookupRule.unapply(key).flatMap(rule => applyRule(rule, t0, t1))
139-
}
140-
14199
/** Checks if adjacency overrides a -> b and b -> c exist (in that order and
142100
* direction) (where b is surrogate tile) and matches expected result
143101
* aExpected, cExpected.
144102
*/
145103
def connectingOrthOverridesExist(lookupRule: PartialFunction[EquivRule, Rule[IdTile]], a: IdTile, b: IdTile, c: IdTile, aExpected: IdTile, cExpected: IdTile): Boolean = {
146-
evaluateRulesOnce(lookupRule, a, b) match {
104+
Rul2Model.evaluateRulesOnce(lookupRule, a, b) match {
147105
case Some((a1, b1)) if a1 == a && a1.id != b1.id =>
148-
evaluateRulesOnce(lookupRule, b1, c) match {
106+
Rul2Model.evaluateRulesOnce(lookupRule, b1, c) match {
149107
case Some((b2, c2)) if b2 == b1 && b2.id != c2.id && c2 != c =>
150108
// found two overrides connecting a to c, so check if result of their application is as expected
151109
a1 == aExpected && c2 == cExpected
@@ -161,15 +119,15 @@ object RedundantAdjacenciesChecker {
161119
* northbound direction.
162120
*/
163121
def connectingDiagOverridesExist(lookupRule: PartialFunction[EquivRule, Rule[IdTile]], a: IdTile, b: IdTile, c: IdTile, d: IdTile, southBound: Boolean, aExpected: IdTile, dExpected: IdTile): Boolean = {
164-
evaluateRulesOnce(lookupRule, a, b) match {
122+
Rul2Model.evaluateRulesOnce(lookupRule, a, b) match {
165123
case Some((a1, b1)) if a1 == a && a1.id != b1.id =>
166124
val rot = if (southBound) R3F0 else R1F0
167-
evaluateRulesOnce(lookupRule, b1 * rot, c * rot) match {
125+
Rul2Model.evaluateRulesOnce(lookupRule, b1 * rot, c * rot) match {
168126
case Some((b2rot, c2rot)) =>
169127
val b2 = b2rot * (R0F0 / rot)
170128
val c2 = c2rot * (R0F0 / rot)
171129
if (b2 == b1 && c2 != c) {
172-
evaluateRulesOnce(lookupRule, c2, d) match {
130+
Rul2Model.evaluateRulesOnce(lookupRule, c2, d) match {
173131
case Some((c3, d3)) if c3 == c2 && d3.id != c3.id && d3.id != b2.id && d3 != d =>
174132
a1 == aExpected && d3 == dExpected
175133
case _ => false

0 commit comments

Comments
 (0)