Merge branch 'main' of github.com:leanprover-community/lean4game

This commit is contained in:
Jon Eugster
2023-02-13 17:48:18 +01:00
12 changed files with 238 additions and 139 deletions
+49 -12
View File
@@ -250,11 +250,6 @@ elab "NewLemmas" args:ident* : command => do
/-! ## Make Game -/
def getTacticDoc! (tac : Name) : CommandElabM TacticDocEntry := do
match (tacticDocExt.getState (← getEnv)).find? (·.name = tac) with
| some doc => return doc
| none => throwError "Tactic documentation not found: {tac}"
/-- Make the final Game. This command will precompute various things about the game, such as which
tactics are available in each level etc. -/
elab "MakeGame" : command => do
@@ -264,30 +259,72 @@ elab "MakeGame" : command => do
if game.worlds.hasLoops then
throwError "World graph has loops!"
-- Compute which tactics are available in which level:
-- Compute which tactics/lemmas are available in which level:
let mut newTacticsInWorld : HashMap Name (HashSet Name) := {}
let mut allTactics : HashSet Name := {}
let mut newLemmasInWorld : HashMap Name (HashSet Name) := {}
let mut allLemmas : HashSet Name := {}
for (worldId, world) in game.worlds.nodes.toArray do
let mut newTactics : HashSet Name:= {}
let mut newLemmas : HashSet Name:= {}
for (_, level) in world.levels.toArray do
newTactics := newTactics.insertMany level.newTactics
allTactics := allTactics.insertMany level.newTactics
newLemmas := newLemmas.insertMany level.newLemmas
allLemmas := allLemmas.insertMany level.newLemmas
newTacticsInWorld := newTacticsInWorld.insert worldId newTactics
newLemmasInWorld := newLemmasInWorld.insert worldId newLemmas
let mut tacticsInWorld : HashMap Name (HashSet Name) := {}
-- Basic tactic/lemma availability: all locked, none disabled.
let Availability₀ : HashMap Name Availability :=
HashMap.ofList $
allTactics.toList.map fun name =>
(name, {name, locked := true, disabled := false})
let lemmaAvailability₀ : HashMap Name Availability :=
HashMap.ofList $
allLemmas.toList.map fun name =>
(name, {name, locked := true, disabled := false})
-- Availability after a given world
let mut tacticsInWorld : HashMap Name (HashMap Name Availability) := {}
let mut lemmasInWorld : HashMap Name (HashMap Name Availability) := {}
for (worldId, _) in game.worlds.nodes.toArray do
let mut tactics : HashSet Name:= {}
let mut tactics := Availability₀
let mut lemmas := lemmaAvailability₀
let predecessors := game.worlds.predecessors worldId
for predWorldId in predecessors do
tactics := tactics.insertMany (newTacticsInWorld.find! predWorldId)
for tac in newTacticsInWorld.find! predWorldId do
tactics := tactics.insert tac {name := tac, locked := false, disabled := false}
for lem in newLemmasInWorld.find! predWorldId do
lemmas := lemmas.insert lem {name := lem, locked := false, disabled := false}
tacticsInWorld := tacticsInWorld.insert worldId tactics
lemmasInWorld := lemmasInWorld.insert worldId lemmas
for (worldId, world) in game.worlds.nodes.toArray do
let mut tactics := tacticsInWorld.find! worldId
logInfo m!"{tactics.toArray}"
let mut lemmas := lemmasInWorld.find! worldId
let levels := world.levels.toArray.insertionSort fun a b => a.1 < b.1
for (levelId, level) in levels do
tactics := tactics.insertMany level.newTactics
for tac in level.newTactics do
tactics := tactics.insert tac {name := tac, locked := false, disabled := false}
for tac in level.disabledTactics do
tactics := tactics.insert tac {name := tac, locked := false, disabled := true}
for lem in level.newLemmas do
lemmas := lemmas.insert lem {name := lem, locked := false, disabled := false}
for lem in level.disabledLemmas do
lemmas := lemmas.insert lem {name := lem, locked := false, disabled := true}
let tacticArray := tactics.toArray
|>.insertionSort (fun a b => a.1.toString < b.1.toString)
|>.map (·.2)
let lemmaArray := lemmas.toArray
|>.insertionSort (fun a b => a.1.toString < b.1.toString)
|>.map (·.2)
modifyLevel ⟨← getCurGameId, worldId, levelId⟩ fun level => do
return {level with tactics := ← tactics.toArray.mapM getTacticDoc!}
return {level with
tactics := tacticArray
lemmas := lemmaArray}
@@ -41,6 +41,10 @@ initialize tacticDocExt : SimplePersistentEnvExtension TacticDocEntry (Array Tac
addImportedFn := Array.concatMap id
}
def getTacticDoc? {m : Type → Type} [Monad m] [MonadEnv m] (tac : Name) :
m (Option TacticDocEntry) := do
return (tacticDocExt.getState (← getEnv)).find? (·.name = tac)
open Elab Command in
/-- Print a registered tactic doc for debugging purposes. -/
elab "#print_tactic_doc" : command => do
@@ -64,6 +68,10 @@ initialize lemmaDocExt : SimplePersistentEnvExtension LemmaDocEntry (Array Lemma
addImportedFn := Array.concatMap id
}
def getLemmaDoc? {m : Type → Type} [Monad m] [MonadEnv m] (tac : Name) :
m (Option LemmaDocEntry) := do
return (lemmaDocExt.getState (← getEnv)).find? (·.name = tac)
open Elab Command in
/-- Print a lemma doc for debugging purposes. -/
elab "#print_lemma_doc" : command => do
@@ -144,6 +152,12 @@ structure LevelId where
level : Nat
deriving Inhabited
structure Availability where
name : Name
locked : Bool
disabled : Bool
deriving ToJson, FromJson, Repr
def getCurLevelId [MonadError m] : m LevelId := do
return { game := ← getCurGameId, world := ← getCurWorldId, level := ← getCurLevelIdx}
@@ -170,7 +184,7 @@ structure GameLevel where
-- only these tactics are allowed in this level (ignore if empty):
onlyTactics: Array Name := default
-- tactics in this level (computed by `MakeGame`):
tactics: Array TacticDocEntry := default
tactics: Array Availability := default
-- new lemmas introduces by this level:
newLemmas: Array Name := default
-- lemmas exceptionally forbidden in this level:
@@ -178,7 +192,7 @@ structure GameLevel where
-- only these lemmas are allowed in this level (ignore if empty):
onlyLemmas: Array Name := default
-- lemmas in this level (computed by `MakeGame`):
lemmas: Array LemmaDocEntry := default
lemmas: Array Availability := default
hints: Array GoalHintEntry := default
goal : TSyntax `Lean.Parser.Command.declSig := default
descrText: String := default
+2 -1
View File
@@ -76,7 +76,8 @@ partial def findForbiddenTactics (inputCtx : Parser.InputContext) (level : GameL
findForbiddenTactics inputCtx level arg
| .atom info val =>
-- ignore syntax elements that do not start with a letter
if 0 < val.length ∧ val.data[0]!.isAlpha then
-- and ignore "with" keyword
if 0 < val.length ∧ val.data[0]!.isAlpha ∧ val != "with" then
if ¬ ((level.tactics.map (·.name.toString))).contains val then
addErrorMessage info s!"You have not unlocked the tactic '{val}' yet!"
else if level.disabledTactics.contains val
+41 -4
View File
@@ -41,8 +41,8 @@ Fields:
structure LevelInfo where
index : Nat
title : String
tactics: Array TacticDocEntry
lemmas: Array LemmaDocEntry
tactics: Array Availability
lemmas: Array Availability
introduction : String
descrText : String := ""
descrFormat : String := ""
@@ -53,6 +53,32 @@ structure LoadLevelParams where
level : Nat
deriving ToJson, FromJson
structure Doc where
name: String
text: String
deriving ToJson
inductive TacOrLem := | tactic | «lemma»
deriving ToJson, FromJson
structure LoadDocParams where
name : Name
type : TacOrLem
deriving ToJson, FromJson
def mkDoc (name : Name) (type : TacOrLem) : GameServerM (Option Doc) := do
match type with
| .tactic => do
let doc ← getTacticDoc? name
return doc.map fun doc =>
{ name := doc.name.toString
text := doc.content }
| .lemma => do
let doc ← getLemmaDoc? name
return doc.map fun doc =>
{ name := doc.name.toString
text := doc.content }
partial def handleServerEvent (ev : ServerEvent) : GameServerM Bool := do
match ev with
| ServerEvent.clientMsg msg =>
@@ -68,8 +94,9 @@ partial def handleServerEvent (ev : ServerEvent) : GameServerM Bool := do
let c ← read
let some lvl ← getLevel? {game := s.game, world := p.world, level := p.level}
| do
c.hOut.writeLspResponseError ⟨id, .invalidParams, s!"Level not found: world {p.world}, level {p.level}", none⟩
return true
c.hOut.writeLspResponseError ⟨id, .invalidParams, s!"Level not found: world {p.world}, level {p.level}", none⟩
return true
let levelInfo : LevelInfo :=
{ index := lvl.index,
title := lvl.title,
@@ -80,6 +107,16 @@ partial def handleServerEvent (ev : ServerEvent) : GameServerM Bool := do
introduction := lvl.introduction }
c.hOut.writeLspResponse ⟨id, ToJson.toJson levelInfo⟩
return true
| Message.request id "loadDoc" params =>
let p ← parseParams LoadDocParams (toJson params)
let s ← get
let c ← read
let some doc ← mkDoc p.name p.type
| do
c.hOut.writeLspResponseError ⟨id, .invalidParams, s!"Documentation not found: {p.name}", none⟩
return true
c.hOut.writeLspResponse ⟨id, ToJson.toJson doc⟩
return true
| _ => return false
| _ => return false