split off test game

still need to adapt the call to the lean binary to provide two arguments
This commit is contained in:
Alexander Bentkamp
2022-10-17 17:42:52 +02:00
parent 7563730292
commit d6bd2c98da
24 changed files with 60 additions and 52 deletions
+2
View File
@@ -0,0 +1,2 @@
import GameServer.Commands
import GameServer.Server
@@ -1,7 +1,7 @@
import Lean
import NNG.GameServer.Utils
import NNG.GameServer.EnvExtensions
import GameServer.Utils
import GameServer.EnvExtensions
open Lean Meta
@@ -1,5 +1,5 @@
import NNG.GameServer.HashMapExtension
import NNG.GameServer.SingleValPersistentEnvExtension
import GameServer.HashMapExtension
import GameServer.SingleValPersistentEnvExtension
/-! # Environment extensions
@@ -6,8 +6,8 @@ It is based on lean-gym by Daniel Selsam.
-/
import Lean.Data.Json.Basic
import NNG.GameServer.Utils
import NNG.GameServer.EnvExtensions
import GameServer.Utils
import GameServer.EnvExtensions
open Lean Meta Elab Tactic Std
@@ -223,7 +223,7 @@ where
open System Lean Std in
partial def runGame (GameName : Name) (paths : List FilePath): IO Unit := do
searchPathRef.set paths
let env ← importModules [{ module := `Init : Import }, { module := GameName ++ GameName : Import }] {} 0
let env ← importModules [{ module := `Init : Import }, { module := GameName : Import }] {} 0
let termElabM : TermElabM Unit := do
let levels := levelsExt.getState env
let game := {← gameExt.get with nb_levels := levels.size }
+14 -11
View File
@@ -1,13 +1,16 @@
import NNG.GameServer.Server
import NNG.NNG
import GameServer.Server
def System.FilePath.parent! (fp : System.FilePath) : System.FilePath :=
match fp.parent with
| some path => path
| none => panic! "Couldn't find parent folder"
unsafe def main (args : List String) : IO Unit := do
unsafe def main : IO Unit := do
let build_folder := (← IO.appPath).parent!.parent!
let paths : List System.FilePath := [build_folder/"lib",
(← Lean.findSysroot) / "lib" / "lean"]
Server.runGame `NNG paths
if args.length != 2 then
throw (IO.userError "Expected two arguments: The name of the game module and the path to the game project.")
let out ← IO.Process.output { cwd := args[1]!, cmd := "lake", args := #["env","printenv","LEAN_PATH"] }
if out.exitCode != 0 then
IO.eprintln out.stderr
else
let paths : List System.FilePath := System.SearchPath.parse out.stdout.trim
let currentDir ← IO.currentDir
let paths := paths.map fun p => currentDir / (args[1]! : System.FilePath) / p
Server.runGame (Lean.Name.mkSimple args[0]!) paths
-14
View File
@@ -1,14 +0,0 @@
import NNG.GameServer.Commands
import NNG.MyNat
LemmaDoc zero_add as zero_add in "Addition"
"This lemma says `∀ a : ℕ, 0 + a = a`."
LemmaDoc add_zero as add_zero in "Addition"
"This lemma says `∀ a : ℕ, a + 0 = a`."
LemmaDoc add_succ as add_succ in "Addition"
"This lemma says `∀ a b : ℕ, a + succ b = succ (a + b)`."
LemmaSet addition : "Addition lemmas" :=
zero_add add_zero
-26
View File
@@ -1,26 +0,0 @@
import NNG.Metadata
Level 1
Title "The reflexivity spell"
Introduction
"
Let's learn a first spell: the `rfl` spell. `rfl` stands for \"reflexivity\", which is a fancy
way of saying that it will prove any goal of the form `A = A`. It doesn't matter how
complicated `A` is, all that matters is that the left hand side is *exactly equal* to the
right hand side (a computer scientist would say \"definitionally equal\"). I really mean
\"press the same buttons on your computer in the same order\" equal.
For example, `x * y + z = x * y + z` can be proved by `rfl`, but `x + y = y + x` cannot.
This is a very low level spell, but you need to start somewhere.
After closing this message, type rfl in the invocation zone and hit Enter or click
the \"Cast spell\" button.
"
Statement (x y z : ℕ) : x * y + z = x * y + z := by
rfl
Conclusion "Congratulations for completing your first level! You can now click on the *Go to next level* button."
Tactics rfl
-36
View File
@@ -1,36 +0,0 @@
import NNG.Metadata
Level 2
Title "The rewriting spell"
Introduction
"
The rewrite spell is the way to \"substitute in\" the value
of an expression. In general, if you have a hypothesis of the form `A = B`, and your
goal mentions the left hand side `A` somewhere, then
the `rewrite` tactic will replace the `A` in your goal with a `B`.
The documentation for `rewrite` just appeared in your spell book.
Play around with the menus and see what is there currently.
More information will appear as you progress.
Take a look in the top right box at what we have.
The variables $x$ and $y$ are natural numbers, and we have
an assumption `h` that $y = x + 7$. Our goal
is to prove that $2y=2(x+7)$. This goal is obvious -- we just
substitute in $y = x+7$ and we're done. In Lean, we do
this substitution using the `rewrite` spell. This spell takes a list of equalities
or equivalences so you can cast `rewrite [h]`.
"
Statement (x y : ℕ) (h : y = x + 7) : 2 * y = 2 * (x + 7) := by
rewrite [h]
rfl
Message (x : ℕ) (y : ℕ) (h : y = x + 7) : 2*(x + 7) = 2*(x + 7) =>
"Great! Now the goal should be easy to reach using the `rfl` spell."
Conclusion "Congratulations for completing your second level!"
Tactics rfl rewrite
-81
View File
@@ -1,81 +0,0 @@
import NNG.Metadata
Level 3
Title "Peano's axioms"
Introduction
"
The team that salvaged the type `ℕ` of natural numbers actually got us three things:
* a term `0 : ℕ`, interpreted as the number zero.
* a function `succ : ℕ → ℕ`, with `succ n` interpreted as \"the number after $n$\".
* The principle of mathematical induction.
These are essentially the axioms isolated by Peano which uniquely characterise
the natural numbers (we also need recursion, but we can ignore it for now).
The first axiom says that $0$ is a natural number. The second says that there
is a `succ` function which eats a number and spits out the number after it,
so $\\operatorname{succ}(0)=1$, $\\operatorname{succ}(1)=2$ and so on.
Peano's last axiom is the principle of mathematical induction. This is a deeper
fact. It says that if we have infinitely many true/false statements $P(0)$, $P(1)$,
$P(2)$ and so on, and if $P(0)$ is true, and if for every natural number $d$
we know that $P(d)$ implies $P(\\operatorname{succ}(d))$, then $P(n)$ must be true for every
natural number $n$. It's like saying that if you have a long line of dominoes, and if
you knock the first one down, and if you know that if a domino falls down then the one
after it will fall down too, then you can deduce that all the dominos will fall down.
One can also think of it as saying that every natural number
can be built by starting at `0` and then applying `succ` a finite number of times.
Peano's insights were firstly that these axioms completely characterise
the natural numbers, and secondly that these axioms alone can be used to build
a whole bunch of other structure on the natural numbers, for example
addition, multiplication and so on.
This game is all about seeing how far these axioms of Peano can take us.
Let's practice our use of the `rewrite` tactic in the following example.
Our hypothesis `h` is a proof that `succ(a) = b` and we want to prove that
`succ(succ(a))=succ(b)`. In words, we're going to prove that if
`b` is the number after `a` then `succ(b)` is the number after `succ(a)`.
Note that the system drops brackets when they're not
necessary, so `succ b` just means `succ(b)`.
Now here's a tricky question. Knowing that our goal is `succ (succ a) = succ b`,
and our assumption is `h : succ a = b`, then what will the goal change
to when we type
`rewrite [h]`
and hit enter? Remember that `rewrite [h]` will
look for the *left* hand side of `h` in the goal, and will replace it with
the *right* hand side. Try and figure out how the goal will change, and
then try it.
"
Statement (a b : ℕ) (h : succ a = b) : succ (succ a) = succ b := by
rewrite [h]
rfl
Message (a : ℕ) (b : ℕ) (h : succ a = b) : succ b = succ b =>
"
Look: Lean changed `succ a` into `b`, so the goal became `succ b = succ b`.
That goal is of the form `X = X`, so you know what to do.
"
Conclusion "Congratulations for completing the third level!
You may be wondering whether we could have just substituted in the definition of `b`
and proved the goal that way. To do that, we would want to replace the right hand
side of `h` with the left hand side. You do this in Lean by writing `rewrite [<- h]`. You get the
left-arrow by typing `\\l` and then a space; note that this is a small letter L,
not a number 1. You can just edit your proof and try it.
You may also be wondering why we keep writing `succ(b)` instead of `b+1`. This
is because we haven't defined addition yet! On the next level, the final level
of the tutorial, we will introduce addition, and then
we'll be ready to enter Addition World.
"
Tactics rfl rewrite
-62
View File
@@ -1,62 +0,0 @@
import NNG.Metadata
Level 4
Title "Addition"
Introduction
"
Peano defined addition `a + b` by induction on `b`, or,
more precisely, by *recursion* on `b`. He first explained how to add 0 to a number:
this is the base case.
* `add_zero (a : ℕ) : a + 0 = a`
We will call this theorem `add_zero`. It has just appeared in your inventory!
Mathematicians sometimes call it \"Lemma 2.1\" or \"Hypothesis P6\" or something. But
computer scientists call it `add_zero` because it tells you
what the answer to \"$x$ add zero\" is. It's a *much* better name than \"Lemma 2.1\".
Even better, we can use the rewrite tactic with `add_zero`.
If you ever see `x + 0` in your goal, `rewrite [add_zero]` will simplify it to `x`.
This is because `add_zero` is a proof that `x + 0 = x` (more precisely,
`add_zero x` is a proof that `x + 0 = x` but Lean can figure out the `x` from the context).
Now here's the inductive step. If you know how to add `d` to `a`, then
Peano tells you how to add `succ(d)` to `a`. It looks like this:
* `add_succ (a d : ℕ) : a + succ(d) = succ (a + d)`
What's going on here is that we assume `a + d` is already
defined, and we define `a + succ(d)` to be the number after it.
This is also in your inventory now -- `add_succ` tells you
how to add a successor to something. If you ever see `... + succ ...`
in your goal, you should be able to use `rewrite [add_succ]` to make
progress. Here is a simple example where we shall see both. Let's prove
that $x$ add the number after $0$ is the number after $x$.
Observe that the goal mentions `... + succ ...`. So type
`rewrite [add_succ]`
and hit enter; see the goal change.
"
Statement (a : ℕ ) : a + succ 0 = succ a := by
rewrite [add_succ]
rewrite [add_zero]
rfl
Message (a : ℕ) : succ (a + 0) = succ a => "
Do you see that the goal now mentions ` ... + 0 ...`? So type
`rewrite [add_zero]`
and try to finish the level alone from there.
"
Conclusion "Congratulations for completing your fourth level! This is the end of the tutorial part
of the game. Serious things start in the next level."
Tactics rfl rewrite
Lemmas add_succ add_zero
-125
View File
@@ -1,125 +0,0 @@
import NNG.Metadata
import NNG.Tactics
Level 5
Title "The induction_on spell"
Introduction
"
Welcome to Addition World. If you've done all four levels in tutorial world
and know about `rewrite` and `rfl`, then you're in the right place. Here's
a reminder of the things you're now equipped with which we'll need in this world.
## Data:
* a type called `ℕ`
* a term `0 : ℕ`, interpreted as the number zero.
* a function `succ : ℕ → ℕ`, with `succ n` interpreted as \"the number after `n`\".
* Usual numerical notation 0,1,2 etc (although 2 onwards will be of no use to us until much later ;-) ).
* Addition (with notation `a + b`).
## Theorems:
* `add_zero (a : ℕ) : a + 0 = a`. Use with `rewrite [add_zero]`.
* `add_succ (a b : ℕ) : a + succ(b) = succ(a + b)`. Use with `rewrite [add_succ]`.
* The principle of mathematical induction. Use with `induction_on` (see below)
## Spells:
* `rfl` : proves goals of the form `X = X`
* `rewrite [h]` : if h is a proof of `A = B`, changes all A's in the goal to B's.
* `induction_on n with d hd` : we're going to learn this right now.
# Important thing:
This is a *really* good time to check you understand about the spell book and the inventory on
the left. Eveything you need is collected in those lists. They
will prove invaluable as the number of theorems we prove gets bigger. On the other hand,
we only need to learn one more spell to really start going places, so let's learn about
that spell right now.
OK so let's see induction in action. We're going to prove
`zero_add (n : ℕ) : 0 + n = n`.
That is: for all natural numbers $n$, $0+n=n$. Wait $-$ what is going on here?
Didn't we already prove that adding zero to $n$ gave us $n$?
No we didn't! We proved $n + 0 = n$, and that proof was called `add_zero`. We're now
trying to establish `zero_add`, the proof that $0 + n = n$. But aren't these two theorems
the same? No they're not! It is *true* that `x + y = y + x`, but we haven't
*proved* it yet, and in fact we will need both `add_zero` and `zero_add` in order
to prove this. In fact `x + y = y + x` is the boss level for addition world,
and `induction_on` is the only other spell you'll need to beat it.
Now `add_zero` is one of Peano's axioms, so we don't need to prove it, we already have it
(indeed, if you've opened the Addition World theorem statements on the left, you can even see it).
To prove `0 + n = n` we need to use induction on $n$. While we're here,
note that `zero_add` is about zero add something, and `add_zero` is about something add zero.
The names of the proofs tell you what the theorems are. Anyway, let's prove `0 + n = n`.
Start by casting `induction_on n`.
"
Statement (n : ℕ) : 0 + n = n := by
induction_on n
rewrite [add_zero]
rfl
rewrite [add_succ]
rewrite [ind_hyp]
rfl
Message : (0 : ℕ) + 0 = 0 => "
We now have *two goals!* The
induction spell has generated for us a base case with `n = 0` (the goal at the top)
and an inductive step (the goal underneath). The golden rule: **spells operate on the current goal** --
the goal at the top. So let's just worry about that top goal now, the base case `0 + 0 = 0`.
Remember that `add_zero` (the proof we have already) is the proof of `x + 0 = x`
(for any $x$) so we can try
`rewrite [add_zero]`
What do you think the goal will change to? Remember to just keep
focussing on the top goal, ignore the other one for now, it's not changing
and we're not working on it.
"
Message (n : ℕ) (ind_hyp: 0 + n = n) : 0 + succ n = succ n =>
"
Great! You solved the base case. We are now be back down
to one goal -- the inductive step.
We have a fixed natural number `n`, and the inductive hypothesis `ind_hyp : 0 + n = n`
saying that we have a proof of `0 + n = n`.
Our goal is to prove `0 + succ n = succ n`. In words, we're showing that
if the lemma is true for `n`, then it's also true for the number after `n`.
That's the inductive step. Once we've proved this inductive step, we will have proved
`zero_add` by the principle of mathematical induction.
To prove our goal, we need to use `add_succ`. We know that `add_succ 0 n`
is the result that `0 + succ n = succ (0 + n)`, so the first thing
we need to do is to replace the left hand side `0 + succ n` of our
goal with the right hand side. We do this with the `rewrite` spell. You can write
`rewrite [add_succ]`
(or even `rewrite [add_succ 0 n]` if you want to give Lean all the inputs instead of making it
figure them out itself).
"
Message (n : ℕ) (ind_hyp: 0 + n = n) : succ (0 + n) = succ n =>
"Well-done! We're almost there. It's time to use our induction hypothesis.
Cast
`rewrite [ind_hyp]`
and finish by yourself.
"
Conclusion "Congratulations for completing your first inductive proof!"
Tactics rfl rewrite induction_on
Lemmas add_succ add_zero
-21
View File
@@ -1,21 +0,0 @@
import NNG.GameServer.Commands
import NNG.MyNat
import NNG.TacticDocs
import NNG.LemmaDocs
Game "NNG"
Title "The Natural Number Game"
Introduction
"This is a sad day for mathematics. While trying to find glorious new foundations for mathematics,
someone removed the law of excluded middle and the axiom of choice. Unsurprisingly,
everything collapsed. A brave rescue team managed to retrieve our precious axioms from the wreckage
but now we need to rebuild all of mathematics from scratch.
As a beginning mathematics wizard, you've been tasked to rebuild the theory of natural numbers from
the axioms that Giuseppe Peano found under the collapsed tower of number theory. You've been equipped
with a level 1 spell book. Good luck."
Conclusion
"There is nothing else so far. Thanks for rescuing natural numbers!"
-20
View File
@@ -1,20 +0,0 @@
axiom MyNat : Type
notation "ℕ" => MyNat
--axiom zero : ℕ
axiom succ : ℕ → ℕ
@[instance] axiom MyOfNat (n : Nat) : OfNat ℕ n
@[instance] axiom myAddition : HAdd ℕ ℕ ℕ
@[instance] axiom myMultiplication : HMul ℕ ℕ ℕ
axiom add_zero : ∀ a : ℕ, a + 0 = a
axiom add_succ : ∀ a b : ℕ, a + succ b = succ (a + b)
@[elabAsElim] axiom myInduction {P : ℕ → Prop} (n : ℕ) (h₀ : P 0) (h : ∀ n, P n → P (succ n)) : P n
-7
View File
@@ -1,7 +0,0 @@
import NNG.Metadata
import NNG.Levels.Level1
import NNG.Levels.Level2
import NNG.Levels.Level3
import NNG.Levels.Level4
import NNG.Levels.Level5
-148
View File
@@ -1,148 +0,0 @@
import NNG.GameServer.Commands
import NNG.Tactics
TacticDoc rfl
"
## Summary
`rfl` proves goals of the form `X = X`.
## Details
The `rfl` tactic will close any goal of the form `A = B`
where `A` and `B` are *exactly the same thing*.
### Example:
If it looks like this in the top right hand box:
```
Objects
a b c d : ℕ
Prove:
(a + b) * (c + d) = (a + b) * (c + d)
```
then
`rfl`
will close the goal and solve the level."
TacticDoc induction_on
"
## Summary
If `n : ℕ` is in our objects list, then `induction_on n`
attempts to prove the current goal by induction on `n`, with the inductive
assumption in the `succ` case being `ind_hyp`.
### Example:
If your current goal is:
```
Objects
n : ℕ
Prove:
2 * n = n + n
```
then
`induction_on n`
will give us two goals:
```
Prove:
2 * 0 = 0 + 0
```
and
```
Objects
n : ℕ,
Assumptions
ind_hyp : 2 * n = n + n
Prove:
2 * succ n = succ n + succ n
```
"
TacticDoc rewrite
"
## Summary
If `h` is a proof of `X = Y`, then `rewrite [h],` will change
all `X`s in the goal to `Y`s. Variants: `rewrite [<- h]` (changes
`Y` to `X`) and
`rewrite [h] at h2` (changes `X` to `Y` in hypothesis `h2` instead
of the goal).
## Details
The `rewrite` tactic is a way to do \"substituting in\". There
are two distinct situations where use this tactics.
1) If `h : A = B` is a hypothesis (i.e., a proof of `A = B`)
in your local context (the box in the top right)
and if your goal contains one or more `A`s, then `rewrite h`
will change them all to `B`'s.
2) The `rewrite` tactic will also work with proofs of theorems
which are equalities (look for them in the inventory).
For example, if your inventory contains `add_zero x : x + 0 = x`,
then `rewrite [add_zero]` will change `x + 0` into `x` in your goal
(or fail with an error if Lean cannot find `x + 0` in the goal).
Important note: if `h` is not a proof of the form `A = B`
or `A ↔ B` (for example if `h` is a function, an implication,
or perhaps even a proposition itself rather than its proof),
then `rewrite` is not the tactic you want to use. For example,
`rewrite [P = Q]` is never correct: `P = Q` is the true-false
statement itself, not the proof.
If `h : P = Q` is its proof, then `rewrite [h]` will work.
Pro tip 1: If `h : A = B` and you want to change
`B`s to `A`s instead, try `rewrite [<- h]` (get the arrow with `\\l` and
note that this is a small letter L, not a number 1).
### Example:
If it looks like this in the top right hand box:
```
Objects
x y : ℕ
Assumptions
h : x = y + y
Prove:
succ (x + 0) = succ (y + y)
```
then
`rewrite [add_zero]`
will change the goal into `succ x = succ (y + y)`, and then
`rewrite [h]`
will change the goal into `succ (y + y) = succ (y + y)`, which
can be solved with `rfl,`.
### Example:
You can use `rewrite` to change a hypothesis as well.
For example, if your local context looks like this:
```
Objects
x y : ℕ
Assumptions
h1 : x = y + 3
h2 : 2 * y = x
Prove:
y = 3
```
then `rewrite [h1] at h2` will turn `h2` into `h2 : 2 * y = y + 3`.
"
TacticDoc intro
"Useful to introduce stuff"
TacticSet basics := rfl induction_on intro rewrite
-12
View File
@@ -1,12 +0,0 @@
import Lean
import NNG.MyNat
open Lean Elab Tactic
elab "swap" : tactic => do
match ← getGoals with
| g₁::g₂::t => setGoals (g₂::g₁::t)
| _ => pure ()
macro "induction_on" n:ident : tactic =>
`(tactic| refine myInduction $n ?base ?inductive_step; swap; clear $n; intro $n $(mkIdent `ind_hyp); swap)
+3 -11
View File
@@ -1,20 +1,12 @@
import Lake
open Lake DSL
package nng {
-- add package configuration options here
}
package GameServer
lean_lib NNG {
-- add library configuration options here
}
lean_lib NNG.levels {
-- add library configuration options here
}
lean_lib GameServer
@[defaultTarget]
lean_exe nng {
lean_exe gameserver {
root := `Main
supportInterpreter := true
}