pull/53/head
Marcus Zibrowius 3 years ago
commit 588c362aa3

@ -38,7 +38,7 @@ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash
source ~/.bashrc source ~/.bashrc
nvm install node npm nvm install node npm
sudo npm install -g http-server npm install -g http-server
``` ```
# Clone NNG interface # Clone NNG interface

@ -6,17 +6,14 @@ import { faLock, faLockOpen, faBook, faHammer, faBan } from '@fortawesome/free-s
import Markdown from './Markdown'; import Markdown from './Markdown';
import { useLoadDocQuery, ComputedInventoryItem } from '../state/api'; import { useLoadDocQuery, ComputedInventoryItem } from '../state/api';
function Inventory({ tactics, lemmas, definitions } : export function Inventory({ tactics, lemmas, definitions, setInventoryDoc } :
{lemmas: ComputedInventoryItem[], {lemmas: ComputedInventoryItem[],
tactics: ComputedInventoryItem[], tactics: ComputedInventoryItem[],
definitions: ComputedInventoryItem[]}) { definitions: ComputedInventoryItem[],
setInventoryDoc: (inventoryDoc: {name: string, type: string}) => void}) {
const [docName, setDocName] = useState(null)
const [docType, setDocType] = useState(null)
function openDoc(name, type) { function openDoc(name, type) {
setDocName(name) setInventoryDoc({name, type})
setDocType(type)
} }
return ( return (
@ -31,8 +28,6 @@ function Inventory({ tactics, lemmas, definitions } :
<h2>Lemmas</h2> <h2>Lemmas</h2>
<InventoryList items={lemmas} docType="Lemma" openDoc={openDoc} /> <InventoryList items={lemmas} docType="Lemma" openDoc={openDoc} />
{docName && <Documentation name={docName} type={docType} />}
</div> </div>
) )
} }
@ -81,7 +76,7 @@ function InventoryItem({name, locked, disabled, showDoc}) {
return <div className={`item ${className}`} onClick={handleClick}>{icon} {name}</div> return <div className={`item ${className}`} onClick={handleClick}>{icon} {name}</div>
} }
function Documentation({name, type}) { export function Documentation({name, type}) {
const doc = useLoadDocQuery({type: type, name: name}) const doc = useLoadDocQuery({type: type, name: name})
@ -90,5 +85,3 @@ function Documentation({name, type}) {
<Markdown>{doc.data?.text}</Markdown> <Markdown>{doc.data?.text}</Markdown>
</> </>
} }
export default Inventory;

@ -9,7 +9,7 @@ import { Link, useParams } from 'react-router-dom';
import { Box, CircularProgress, FormControlLabel, FormGroup, Switch, IconButton } from '@mui/material'; import { Box, CircularProgress, FormControlLabel, FormGroup, Switch, IconButton } from '@mui/material';
import MuiDrawer from '@mui/material/Drawer'; import MuiDrawer from '@mui/material/Drawer';
import Grid from '@mui/material/Unstable_Grid2'; import Grid from '@mui/material/Unstable_Grid2';
import Inventory from './Inventory'; import {Inventory, Documentation} from './Inventory';
import { LeanTaskGutter } from 'lean4web/client/src/editor/taskgutter'; import { LeanTaskGutter } from 'lean4web/client/src/editor/taskgutter';
import { AbbreviationProvider } from 'lean4web/client/src/editor/abbreviation/AbbreviationProvider'; import { AbbreviationProvider } from 'lean4web/client/src/editor/abbreviation/AbbreviationProvider';
import 'lean4web/client/src/editor/vscode.css'; import 'lean4web/client/src/editor/vscode.css';
@ -61,6 +61,17 @@ function Level() {
const levelId = parseInt(params.levelId) const levelId = parseInt(params.levelId)
const worldId = params.worldId const worldId = params.worldId
useLoadWorldFiles(worldId)
if (levelId == 0) {
return <Introduction worldId={worldId} />
} else {
return <PlayableLevel worldId={worldId} levelId={levelId} />
}
}
function PlayableLevel({worldId, levelId}) {
const codeviewRef = useRef<HTMLDivElement>(null) const codeviewRef = useRef<HTMLDivElement>(null)
const introductionPanelRef = useRef<HTMLDivElement>(null) const introductionPanelRef = useRef<HTMLDivElement>(null)
@ -115,8 +126,6 @@ function Level() {
const gameInfo = useGetGameInfoQuery() const gameInfo = useGetGameInfoQuery()
useLoadWorldFiles(worldId)
const level = useLoadLevelQuery({world: worldId, level: levelId}) const level = useLoadLevelQuery({world: worldId, level: levelId})
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
@ -132,79 +141,14 @@ function Level() {
const {editor, infoProvider, editorConnection} = const {editor, infoProvider, editorConnection} =
useLevelEditor(worldId, levelId, codeviewRef, initialCode, onDidChangeContent) useLevelEditor(worldId, levelId, codeviewRef, initialCode, onDidChangeContent)
// TODO: This is a hack for having an introduction (i.e. level 0) const [inventoryDoc, setInventoryDoc] = useState<{name: string, type: string}>(null)
// for each world.
if (levelId == 0) { const levelTitle = <>{levelId && `Level ${levelId}`}{level?.data?.title && `: ${level?.data?.title}`}</>
return <>
<div className="app-bar">
<div>
<Button to={`/`}><FontAwesomeIcon icon={faHome} /></Button>
<span className="app-bar-title">
{gameInfo.data?.worlds.nodes[worldId].title && `World: ${gameInfo.data?.worlds.nodes[worldId].title}`}
</span>
</div>
<div>
<span className="app-bar-title">
{`Einführung`}
</span>
<Button disabled={levelId <= 1} inverted={true}
to={`/world/${worldId}/level/0`}
><FontAwesomeIcon icon={faArrowLeft} />&nbsp;Previous</Button>
<Button disabled={levelId >= gameInfo.data?.worldSize[worldId]} inverted={true}
to={`/world/${worldId}/level/1`}
>Next&nbsp;<FontAwesomeIcon icon={faArrowRight} /></Button>
</div>
</div>
<div className="exercise-panel">
<div ref={introductionPanelRef} className="introduction-panel">
<Alert severity="info" sx={{ mt: 1 }}>
<Markdown>
{gameInfo.data?.worlds.nodes[worldId].introduction}
</Markdown>
</Alert>
<div ref={codeviewRef} className={`codeview ${commandLineMode ? 'hidden' : ''}`}></div>
</div>
<div className="conclusion">
{levelId >= gameInfo.data?.worldSize[worldId] ?
<Button to={`/`}><FontAwesomeIcon icon={faHome} /></Button> :
<Button to={`/world/${worldId}/level/1`}>
Start&nbsp;<FontAwesomeIcon icon={faArrowRight} />
</Button>}
</div>
</div>
</>
}
return <> return <>
<div style={level.isLoading ? null : {display: "none"}} className="app-content loading"><CircularProgress /></div> <div style={level.isLoading ? null : {display: "none"}} className="app-content loading"><CircularProgress /></div>
<div style={level.isLoading ? {display: "none"} : null} className="app-bar"> <LevelAppBar isLoading={level.isLoading} levelTitle={levelTitle} worldId={worldId} levelId={levelId} />
<div> <Split minSize={0} snapOffset={200} sizes={[50, 25, 25]} className={`app-content level ${level.isLoading ? 'hidden' : ''}`}>
<Button to={`/`}><FontAwesomeIcon icon={faHome} /></Button>
<span className="app-bar-title">
{gameInfo.data?.worlds.nodes[worldId].title && `World: ${gameInfo.data?.worlds.nodes[worldId].title}`}
</span>
</div>
<div>
<span className="app-bar-title">
{levelId && `Level ${levelId}`}{level?.data?.title && `: ${level?.data?.title}`}
</span>
<Button disabled={levelId <= 0} inverted={true}
to={`/world/${worldId}/level/${levelId - 1}`}
><FontAwesomeIcon icon={faArrowLeft} />&nbsp;Previous</Button>
<Button disabled={levelId >= gameInfo.data?.worldSize[worldId]} inverted={true}
to={`/world/${worldId}/level/${levelId + 1}`}
>Next&nbsp;<FontAwesomeIcon icon={faArrowRight} /></Button>
</div>
</div>
<Split minSize={0} snapOffset={200} sizes={[0, 50, 25, 25]} className={`app-content level ${level.isLoading ? 'hidden' : ''}`}>
<div className="side-panel">
{/*
<div ref={introductionPanelRef} className="introduction-panel">
<Markdown>{level?.data?.introduction}</Markdown>
</div>
*/}
</div>
<div className="exercise-panel"> <div className="exercise-panel">
<div ref={introductionPanelRef} className="introduction-panel"> <div ref={introductionPanelRef} className="introduction-panel">
<Alert severity="info" sx={{ mt: 1 }}> <Alert severity="info" sx={{ mt: 1 }}>
@ -241,11 +185,13 @@ function Level() {
</div>} </div>}
</div> </div>
<div className="doc-panel"> <div className="inventory-panel">
{!level.isLoading && <Inventory tactics={level?.data?.tactics} lemmas={level?.data?.lemmas} definitions={level?.data?.definitions} />} {!level.isLoading &&
<Inventory tactics={level?.data?.tactics} lemmas={level?.data?.lemmas}
definitions={level?.data?.definitions} setInventoryDoc={setInventoryDoc} />}
</div> </div>
<div className="side-panel"> <div className="doc-panel">
<p>Display Tactic documentation here?</p> {inventoryDoc && <Documentation name={inventoryDoc.name} type={inventoryDoc.type} />}
</div> </div>
</Split> </Split>
</> </>
@ -253,6 +199,55 @@ function Level() {
export default Level export default Level
function Introduction({worldId}) {
const gameInfo = useGetGameInfoQuery()
return <>
<div style={gameInfo.isLoading ? null : {display: "none"}} className="app-content loading"><CircularProgress /></div>
<LevelAppBar isLoading={gameInfo.isLoading} levelTitle="Einführung" worldId={worldId} levelId={0} />
<div style={gameInfo.isLoading ? {display: "none"} : null} className="exercise-panel">
<div className="introduction-panel">
<Alert severity="info" sx={{ mt: 1 }}>
<Markdown>
{gameInfo.data?.worlds.nodes[worldId].introduction}
</Markdown>
</Alert>
</div>
<div className="conclusion">
{0 == gameInfo.data?.worldSize[worldId] ?
<Button to={`/`}><FontAwesomeIcon icon={faHome} /></Button> :
<Button to={`/world/${worldId}/level/1`}>
Start&nbsp;<FontAwesomeIcon icon={faArrowRight} />
</Button>}
</div>
</div>
</>
}
function LevelAppBar({isLoading, levelId, worldId, levelTitle}) {
const gameInfo = useGetGameInfoQuery()
return <div className="app-bar" style={isLoading ? {display: "none"} : null} >
<div>
<Button to={`/`}><FontAwesomeIcon icon={faHome} /></Button>
<span className="app-bar-title">
{gameInfo.data?.worlds.nodes[worldId].title && `World: ${gameInfo.data?.worlds.nodes[worldId].title}`}
</span>
</div>
<div>
<span className="app-bar-title">
{levelTitle}
</span>
<Button disabled={levelId <= 0} inverted={true}
to={`/world/${worldId}/level/${levelId - 1}`}
><FontAwesomeIcon icon={faArrowLeft} />&nbsp;Previous</Button>
<Button disabled={levelId >= gameInfo.data?.worldSize[worldId]} inverted={true}
to={`/world/${worldId}/level/${levelId + 1}`}
>Next&nbsp;<FontAwesomeIcon icon={faArrowRight} /></Button>
</div>
</div>
}
function useLevelEditor(worldId: string, levelId: number, codeviewRef, initialCode, onDidChangeContent) { function useLevelEditor(worldId: string, levelId: number, codeviewRef, initialCode, onDidChangeContent) {

@ -8,12 +8,6 @@
margin-bottom: .2em; margin-bottom: .2em;
} }
.inventory h2.doc {
margin-top: 1.5em;
font-weight: 900;
}
.inventory-list { .inventory-list {
display: flex; display: flex;
gap: .5em; gap: .5em;
@ -58,3 +52,15 @@
color: black; color: black;
border-bottom: 0.3em solid var(--clr-primary); border-bottom: 0.3em solid var(--clr-primary);
} }
.doc-panel {
padding: 0 1em;
}
.doc-panel h2 {
font-size: 1.5em;
margin-top: 1em;
margin-bottom: .2em;
font-weight: 900;
}

@ -24,17 +24,11 @@
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg=='); background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==');
} }
.side-panel, .exercise-panel, .doc-panel { .inventory-panel, .exercise-panel, .doc-panel {
height: 100%; height: 100%;
overflow: auto; overflow: auto;
} }
.side-panel {
display: flex;
flex-flow: column;
background-color: #e9f2fb;
}
.introduction-panel, .infoview, .exercise, .conclusion { .introduction-panel, .infoview, .exercise, .conclusion {
padding-top: 1em; padding-top: 1em;
padding-left: 1em; padding-left: 1em;

@ -0,0 +1,26 @@
import Lean
section AbstractCtx
open Lean
open Meta
structure AbstractCtxResult :=
(abstractMVarsResult : AbstractMVarsResult)
/-- Abstract LCtx and MCtx to transport an expression into different contexts -/
def abstractCtx (goal : MVarId) : MetaM AbstractCtxResult := do
let goalDecl ← goal.getDecl
let fvars := goalDecl.lctx.decls.toArray.filterMap id |> Array.map (mkFVar ·.fvarId)
let goal ← mkLambdaFVars (usedLetOnly := false) fvars goalDecl.type
let goal ← abstractMVars goal
return {
abstractMVarsResult := goal
}
def openAbstractCtxResult (res : AbstractCtxResult) (k : Array Expr → Expr → MetaM α) : MetaM α := do
let (mvars, binderInfo, expr) ← openAbstractMVarsResult res.abstractMVarsResult
lambdaLetTelescope (← instantiateMVars expr) k
-- TODO: Unfornately, lambdaLetTelescope does not allow us to provide the number of arguments.
-- If the goal is a function, this will not work.
end AbstractCtx

@ -1,6 +1,7 @@
import Lean import Lean
import GameServer.EnvExtensions import GameServer.EnvExtensions
import GameServer.StrInterpolation
open Lean Meta open Lean Meta
@ -61,36 +62,117 @@ partial def reprintCore : Syntax → Option Format
def reprint (stx : Syntax) : Format := def reprint (stx : Syntax) : Format :=
reprintCore stx |>.getD "" reprintCore stx |>.getD ""
-- macro mods:declModifiers "lemma" n:declId sig:declSig val:declVal : command => `($mods:declModifiers theorem $n $sig $val) syntax hintArg := " (" (&"strict" <|> &"hidden") " := " withoutPosition(term) ")"
/-- A tactic that can be used inside `Statement`s to indicate in which proof states players should
see hints. The tactic does not affect the goal state. -/
elab "Hint" args:hintArg* msg:Lean.Parser.interpolatedStrNoIndent : tactic => do
let mut strict := false
let mut hidden := false
for arg in args do
match arg with
| `(hintArg| (strict := true)) => strict := true
| `(hintArg| (strict := false)) => strict := false
| `(hintArg| (hidden := true)) => hidden := true
| `(hintArg| (hidden := false)) => hidden := false
| _ => throwUnsupportedSyntax
let goal ← Tactic.getMainGoal
goal.withContext do
-- We construct an expression that can produce the hint text. The difficulty is that we
-- want the text to possibly contain quotation of the local variables which might have been
-- named differently by the player.
let varsName := `vars
let text ← withLocalDeclD varsName (mkApp (mkConst ``Array [levelZero]) (mkConst ``Expr)) fun vars => do
let mut text ← expandInterpolatedStr ⟨msg.raw⟩ (← `(MessageData)) (← `(toMessageData))
let goalDecl ← goal.getDecl
let decls := goalDecl.lctx.decls.toArray.filterMap id
for i in [:decls.size] do
text ← `(let $(mkIdent decls[i]!.userName) := $(mkIdent varsName)[$(quote i)]!; $text)
return ← mkLambdaFVars #[vars] $ ← elabTermAndSynthesize text none
let textmvar ← mkFreshExprMVar none
guard $ ← isDefEq textmvar text -- Store the text in a mvar.
-- The information about the hint is logged as a message using `logInfo` to transfer it to the
-- `Statement` command defined below:
logInfo $
.tagged `Hint $
.nest (if strict then 1 else 0) $
.nest (if hidden then 1 else 0) $
.compose (.ofGoal textmvar.mvarId!) (.ofGoal goal)
/-- This tactic allows us to execute an alternative sequence of tactics, but without affecting the
proof state. We use it to define Hints for alternative proof methods or dead ends. -/
elab "Branch" t:tacticSeq : tactic => do
let b ← saveState
Tactic.evalTactic t
let msgs ← Core.getMessageLog
b.restore
Core.setMessageLog msgs
/-- Define the statement of the current level. /-- Define the statement of the current level.
Arguments: Arguments:
- ident: (Optional) The name of the statemtent. - ident: (Optional) The name of the statemtent.
- descr: The human-readable version of the lemma as string. Accepts Markdown and Mathjax. - descr: (Optional) The human-readable version of the lemma as string. Accepts Markdown and Mathjax.
-/ -/
elab "Statement" statementName:ident ? descr:str sig:declSig val:declVal : command => do elab "Statement" statementName:ident ? descr:str ? sig:declSig val:declVal : command => do
let lvlIdx ← getCurLevelIdx let lvlIdx ← getCurLevelIdx
let defaultDeclName : Name := (← getCurGame).name ++ (← getCurWorld).name ++ let defaultDeclName : Name := (← getCurGame).name ++ (← getCurWorld).name ++
("level" ++ toString lvlIdx : String) ("level" ++ toString lvlIdx : String)
-- save the messages before evaluation of the proof
let initMsgs ← modifyGet fun st => (st.messages, { st with messages := {} })
let thmStatement ← `(theorem $(mkIdent defaultDeclName) $sig $val) let thmStatement ← `(theorem $(mkIdent defaultDeclName) $sig $val)
-- let thmStatement' ← match statementName with -- let thmStatement' ← match statementName with
-- | none => `(lemma $(mkIdent "XX") $sig $val) -- TODO: Make it into an `example` -- | none => `(lemma $(mkIdent "XX") $sig $val) -- TODO: Make it into an `example`
-- | some name => `(lemma $name $sig $val) -- | some name => `(lemma $name $sig $val)
elabCommand thmStatement
let msgs := (← get).messages
let mut hints := #[]
let mut nonHintMsgs := #[]
for msg in msgs.msgs do
-- Look for messages produced by the `Hint` tactic. They are used to pass information about the
-- intermediate goal state
if let MessageData.withNamingContext _ $ MessageData.withContext ctx $
.tagged `Hint $
.nest strict $
.nest hidden $
.compose (.ofGoal text) (.ofGoal goal) := msg.data then
let hint ← liftTermElabM $ withMCtx ctx.mctx $ withLCtx ctx.lctx #[] $ withEnv ctx.env do
return {
goal := ← abstractCtx goal
text := ← instantiateMVars (mkMVar text)
strict := strict == 1
hidden := hidden == 1
}
hints := hints.push hint
else
nonHintMsgs := nonHintMsgs.push msg
-- restore saved messages and non-hint messages
modify fun st => { st with
messages := initMsgs ++ ⟨nonHintMsgs.toPArray'⟩
}
let scope ← getScope let scope ← getScope
let env ← getEnv let env ← getEnv
elabCommand thmStatement
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
module := env.header.mainModule module := env.header.mainModule
goal := sig, goal := sig,
scope := scope, scope := scope,
descrText := descr.getString, descrText := match descr with
| none => ""
| some s => s.getString
descrFormat := match statementName with descrFormat := match statementName with
| none => "example " ++ (toString <| reprint sig.raw) ++ " := by" | none => "example " ++ (toString <| reprint sig.raw) ++ " := by"
| some name => (Format.join ["lemma ", reprint name.raw, " ", reprint sig.raw, " := by"]).pretty 10 -- "lemma " ++ (toString <| reprint name.raw) ++ " " ++ (Format.pretty (reprint sig.raw) 40) ++ " := by" | some name => (Format.join ["lemma ", reprint name.raw, " ", reprint sig.raw, " := by"]).pretty 10 -- "lemma " ++ (toString <| reprint name.raw) ++ " " ++ (Format.pretty (reprint sig.raw) 40) ++ " := by"
hints := hints
} -- Format.pretty <| format thmStatement.raw } } -- Format.pretty <| format thmStatement.raw }
/-- Define the conclusion of the current game or current level if some /-- Define the conclusion of the current game or current level if some
@ -138,38 +220,40 @@ def mkGoalSyntax (s : Term) : List (Ident × Term) → MacroM Term
| (n, t)::tail => do return (← `(∀ $n : $t, $(← mkGoalSyntax s tail))) | (n, t)::tail => do return (← `(∀ $n : $t, $(← mkGoalSyntax s tail)))
| [] => return s | [] => return s
def elabHint (hidden : Bool) (binders : TSyntaxArray `Lean.Parser.Term.bracketedBinder) -- def elabHint (hidden : Bool) (binders : TSyntaxArray `Lean.Parser.Term.bracketedBinder)
(goal : TSyntax `term) (msg : TSyntax `interpolatedStrKind) := -- (goal : TSyntax `term) (msg : TSyntax `interpolatedStrKind) :=
liftTermElabM do withOptions (fun options => options.setBool `linter.unusedVariables false) do -- liftTermElabM do withOptions (fun options => options.setBool `linter.unusedVariables false) do
let (g, decls) ← elabBinders binders fun xs => do -- let (g, decls) ← elabBinders binders fun xs => do
let g ← mkForallFVars xs $ ← elabTermAndSynthesize goal none -- let g ← mkForallFVars xs $ ← elabTermAndSynthesize goal none
synthesizeSyntheticMVarsNoPostponing false -- synthesizeSyntheticMVarsNoPostponing false
return (← instantiateMVars g, ← xs.mapM (fun x => x.fvarId!.getDecl)) -- return (← instantiateMVars g, ← xs.mapM (fun x => x.fvarId!.getDecl))
let varsName := `vars -- let varsName := `vars
let msg ← withLocalDeclD varsName (mkApp (mkConst ``Array [levelZero]) (mkConst ``Expr)) fun vars => do -- let msg ← withLocalDeclD varsName (mkApp (mkConst ``Array [levelZero]) (mkConst ``Expr)) fun vars => do
let mut msg ← `(m! $msg) -- let mut msg ← `(m! $msg)
for i in [:decls.size] do -- for i in [:decls.size] do
msg ← `(let $(mkIdent decls[i]!.userName) := $(mkIdent varsName)[$(quote i)]!; $msg) -- msg ← `(let $(mkIdent decls[i]!.userName) := $(mkIdent varsName)[$(quote i)]!; $msg)
return ← mkLambdaFVars #[vars] $ ← elabTermAndSynthesize msg none -- return ← mkLambdaFVars #[vars] $ ← elabTermAndSynthesize msg none
if g.hasMVar then throwError m!"Goal contains metavariables: {g}" -- if g.hasMVar then throwError m!"Goal contains metavariables: {g}"
modifyCurLevel fun level => pure {level with hints := level.hints.push { -- modifyCurLevel fun level => pure {level with hints := level.hints.push {
goal := g, -- goal := g,
intros := decls.size, -- intros := decls.size,
hidden := hidden, -- hidden := hidden,
text := msg }} -- text := msg }}
/-- Declare a hint. This version doesn't prevent the unused linter variable from running. -/ /-- Declare a hint. This version doesn't prevent the unused linter variable from running. -/
local elab "Hint'" binders:bracketedBinder* ":" goal:term "=>" msg:interpolatedStr(term) : command => local elab "Hint'" binders:bracketedBinder* ":" goal:term "=>" msg:interpolatedStr(term) : command =>
elabHint false binders goal msg -- elabHint false binders goal msg
pure ()
/-- /--
Declare a hint. This version doesn't prevent the unused linter variable from running. Declare a hint. This version doesn't prevent the unused linter variable from running.
A hidden hint is only displayed if explicitly requested by the user. A hidden hint is only displayed if explicitly requested by the user.
-/ -/
local elab "HiddenHint'" binders:bracketedBinder* ":" goal:term "=>" msg:interpolatedStr(term) : command => do local elab "HiddenHint'" binders:bracketedBinder* ":" goal:term "=>" msg:interpolatedStr(term) : command => do
elabHint true binders goal msg -- elabHint true binders goal msg
pure ()
/-- Declare a hint in reaction to a given tactic state in the current level. -/ /-- Declare a hint in reaction to a given tactic state in the current level. -/
macro "Hint" decls:bracketedBinder* ":" goal:term "=>" msg:interpolatedStr(term) : command => do macro "Hint" decls:bracketedBinder* ":" goal:term "=>" msg:interpolatedStr(term) : command => do
@ -201,19 +285,19 @@ elab "TacticDoc" name:ident content:str : command =>
content := content.getString }) content := content.getString })
/-- Declare tactics that are introduced by this level. -/ /-- Declare tactics that are introduced by this level. -/
elab "NewTactics" args:ident* : command => do elab "NewTactic" args:ident* : command => do
let names := args.map (·.getId) let names := args.map (·.getId)
for name in names do checkInventoryDoc .Tactic name for name in names do checkInventoryDoc .Tactic name
modifyCurLevel fun level => pure {level with tactics := {level.tactics with new := names}} modifyCurLevel fun level => pure {level with tactics := {level.tactics with new := names}}
/-- Declare tactics that are temporarily disabled in this level -/ /-- Declare tactics that are temporarily disabled in this level -/
elab "DisabledTactics" args:ident* : command => do elab "DisabledTactic" args:ident* : command => do
let names := args.map (·.getId) let names := args.map (·.getId)
for name in names do checkInventoryDoc .Tactic name -- for name in names do checkInventoryDoc .Tactic name
modifyCurLevel fun level => pure {level with tactics := {level.tactics with disabled := names}} modifyCurLevel fun level => pure {level with tactics := {level.tactics with disabled := names}}
/-- Temporarily disable all tactics except the ones declared here -/ /-- Temporarily disable all tactics except the ones declared here -/
elab "OnlyTactics" args:ident* : command => do elab "OnlyTactic" args:ident* : command => do
let names := args.map (·.getId) let names := args.map (·.getId)
for name in names do checkInventoryDoc .Tactic name for name in names do checkInventoryDoc .Tactic name
modifyCurLevel fun level => pure {level with tactics := {level.tactics with only := names}} modifyCurLevel fun level => pure {level with tactics := {level.tactics with only := names}}
@ -231,19 +315,19 @@ elab "DefinitionDoc" name:ident content:str : command =>
content := content.getString }) content := content.getString })
/-- Declare definitions that are introduced by this level. -/ /-- Declare definitions that are introduced by this level. -/
elab "NewDefinitions" args:ident* : command => do elab "NewDefinition" args:ident* : command => do
let names := args.map (·.getId) let names := args.map (·.getId)
for name in names do checkInventoryDoc .Definition name for name in names do checkInventoryDoc .Definition name
modifyCurLevel fun level => pure {level with definitions := {level.definitions with new := names}} modifyCurLevel fun level => pure {level with definitions := {level.definitions with new := names}}
/-- Declare definitions that are temporarily disabled in this level -/ /-- Declare definitions that are temporarily disabled in this level -/
elab "DisabledDefinitions" args:ident* : command => do elab "DisabledDefinition" args:ident* : command => do
let names := args.map (·.getId) let names := args.map (·.getId)
for name in names do checkInventoryDoc .Definition name -- for name in names do checkInventoryDoc .Definition name
modifyCurLevel fun level => pure {level with definitions := {level.definitions with disabled := names}} modifyCurLevel fun level => pure {level with definitions := {level.definitions with disabled := names}}
/-- Temporarily disable all definitions except the ones declared here -/ /-- Temporarily disable all definitions except the ones declared here -/
elab "OnlyDefinitions" args:ident* : command => do elab "OnlyDefinition" args:ident* : command => do
let names := args.map (·.getId) let names := args.map (·.getId)
for name in names do checkInventoryDoc .Definition name for name in names do checkInventoryDoc .Definition name
modifyCurLevel fun level => pure {level with definitions := {level.definitions with only := names}} modifyCurLevel fun level => pure {level with definitions := {level.definitions with only := names}}
@ -264,19 +348,19 @@ elab "LemmaDoc" name:ident "as" userName:ident "in" category:str content:str : c
content := content.getString }) content := content.getString })
/-- Declare lemmas that are introduced by this level. -/ /-- Declare lemmas that are introduced by this level. -/
elab "NewLemmas" args:ident* : command => do elab "NewLemma" args:ident* : command => do
let names := args.map (·.getId) let names := args.map (·.getId)
for name in names do checkInventoryDoc .Lemma name for name in names do checkInventoryDoc .Lemma name
modifyCurLevel fun level => pure {level with lemmas := {level.lemmas with new := names}} modifyCurLevel fun level => pure {level with lemmas := {level.lemmas with new := names}}
/-- Declare lemmas that are temporarily disabled in this level -/ /-- Declare lemmas that are temporarily disabled in this level -/
elab "DisabledLemmas" args:ident* : command => do elab "DisabledLemma" args:ident* : command => do
let names := args.map (·.getId) let names := args.map (·.getId)
for name in names do checkInventoryDoc .Lemma name -- for name in names do checkInventoryDoc .Lemma name
modifyCurLevel fun level => pure {level with lemmas := {level.lemmas with disabled := names}} modifyCurLevel fun level => pure {level with lemmas := {level.lemmas with disabled := names}}
/-- Temporarily disable all lemmas except the ones declared here -/ /-- Temporarily disable all lemmas except the ones declared here -/
elab "OnlyLemmas" args:ident* : command => do elab "OnlyLemma" args:ident* : command => do
let names := args.map (·.getId) let names := args.map (·.getId)
for name in names do checkInventoryDoc .Lemma name for name in names do checkInventoryDoc .Lemma name
modifyCurLevel fun level => pure {level with lemmas := {level.lemmas with only := names}} modifyCurLevel fun level => pure {level with lemmas := {level.lemmas with only := names}}
@ -347,13 +431,18 @@ elab "MakeGame" : command => do
for item in (level.getInventory inventoryType).new do for item in (level.getInventory inventoryType).new do
let category := (← getInventoryDoc? item inventoryType).get!.category let category := (← getInventoryDoc? item inventoryType).get!.category
items := items.insert item {name := item, category, locked := false, disabled := false} items := items.insert item {name := item, category, locked := false, disabled := false}
let mut disabled : HashSet Name := {}
for item in (level.getInventory inventoryType).disabled do for item in (level.getInventory inventoryType).disabled do
let category := (← getInventoryDoc? item inventoryType).get!.category let category := (← getInventoryDoc? item inventoryType).get!.category
items := items.insert item {name := item, category, locked := false, disabled := true} items := items.insert item {name := item, category, locked := false, disabled := false}
-- (we set disabled to false at first because it applies only to the current level)
disabled := disabled.insert item
let itemsArray := items.toArray let itemsArray := items.toArray
|>.insertionSort (fun a b => a.1.toString < b.1.toString) |>.insertionSort (fun a b => a.1.toString < b.1.toString)
|>.map (·.2) |>.map (·.2)
|>.map (fun item => {item with disabled := disabled.contains item.name})
modifyLevel ⟨← getCurGameId, worldId, levelId⟩ fun level => do modifyLevel ⟨← getCurGameId, worldId, levelId⟩ fun level => do
return level.setComputedInventory inventoryType itemsArray return level.setComputedInventory inventoryType itemsArray

@ -1,5 +1,6 @@
import Lean import Lean
import GameServer.Graph import GameServer.Graph
import GameServer.AbstractCtx
/-! # Environment extensions /-! # Environment extensions
@ -16,15 +17,17 @@ open Lean
A hint to help the user with a specific goal state A hint to help the user with a specific goal state
-/ -/
structure GoalHintEntry where structure GoalHintEntry where
goal : Expr goal : AbstractCtxResult
/-- Number of variables to intro in the beginning of goal -/
intros : Nat
/-- Text of the hint as an expression of type `Array Expr → MessageData` -/ /-- Text of the hint as an expression of type `Array Expr → MessageData` -/
text : Expr text : Expr
/-- If true, then hint should be hidden by default -/ /-- If true, then hint should be hidden and only be shown on player's request -/
hidden : Bool := false hidden : Bool := false
deriving Repr /-- If true, then the goal must contain only the assumptions specified in `goal` and no others -/
strict : Bool := false
instance : Repr GoalHintEntry := {
reprPrec := fun a n => reprPrec a.text n
}
/-! ## Tactic/Definition/Lemma documentation -/ /-! ## Tactic/Definition/Lemma documentation -/

@ -25,26 +25,83 @@ def getLevelByFileName? [Monad m] [MonadEnv m] (fileName : String) : m (Option G
| return none | return none
return ← getLevel? levelId return ← getLevel? levelId
/-- Check if each meta variable in `declMvars` has a matching fvar in `declFvars` -/ structure FVarBijection :=
def matchDecls (declMvars : Array Expr) (declFvars : Array Expr) : MetaM Bool := do (forward : HashMap FVarId FVarId)
(backward : HashMap FVarId FVarId)
instance : EmptyCollection FVarBijection := ⟨{},{}⟩
def FVarBijection.insert (bij : FVarBijection) (a b : FVarId) : FVarBijection :=
⟨bij.forward.insert a b, bij.backward.insert b a⟩
def FVarBijection.insert? (bij : FVarBijection) (a b : FVarId) : Option FVarBijection :=
let a' := bij.forward.find? a
let b' := bij.forward.find? b
if (a' == none || a' == some b) && (b' == none || b' == some a)
then some $ bij.insert a b
else none
/-- Checks if `pattern` and `e` are equal up to FVar identities. -/
partial def matchExpr (pattern : Expr) (e : Expr) (bij : FVarBijection := {}) : Option FVarBijection :=
match pattern, e with
| .bvar i1, .bvar i2 => if i1 == i2 then bij else none
| .fvar i1, .fvar i2 => bij.insert? i1 i2
| .mvar _, .mvar _ => bij
| .sort u1, .sort u2 => bij -- TODO?
| .const n1 ls1, .const n2 ls2 =>
if n1 == n2 then bij else none -- && (← (ls1.zip ls2).allM fun (l1, l2) => Meta.isLevelDefEq l1 l2)
| .app f1 a1, .app f2 a2 =>
some bij
|> (Option.bind · (fun bij => matchExpr f1 f2 bij))
|> (Option.bind · (fun bij => matchExpr a1 a2 bij))
| .lam _ t1 b1 _, .lam _ t2 b2 _ =>
some bij
|> (Option.bind · (fun bij => matchExpr t1 t2 bij))
|> (Option.bind · (fun bij => matchExpr b1 b2 bij))
| .forallE _ t1 b1 _, .forallE _ t2 b2 _ =>
some bij
|> (Option.bind · (fun bij => matchExpr t1 t2 bij))
|> (Option.bind · (fun bij => matchExpr b1 b2 bij))
| .letE _ t1 v1 b1 _, .letE _ t2 v2 b2 _ =>
some bij
|> (Option.bind · (fun bij => matchExpr t1 t2 bij))
|> (Option.bind · (fun bij => matchExpr v1 v2 bij))
|> (Option.bind · (fun bij => matchExpr b1 b2 bij))
| .lit l1, .lit l2 =>
if l1 == l2 then bij else none
| .proj i1 n1 e1, .proj i2 n2 e2 =>
if i1 == i2 && n1 == n2 then matchExpr e1 e2 bij else none
-- ignore mdata:
| .mdata _ pattern', _ =>
matchExpr pattern' e bij
| _, .mdata _ e' =>
matchExpr pattern e' bij
| _, _ => none
/-- Check if each fvar in `patterns` has a matching fvar in `fvars` -/
def matchDecls (patterns : Array Expr) (fvars : Array Expr) (strict := true) (initBij : FVarBijection := {}) : MetaM Bool := do
-- We iterate through the array backwards hoping that this will find us faster results -- We iterate through the array backwards hoping that this will find us faster results
-- TODO: implement backtracking -- TODO: implement backtracking
let mut usedFvars := (List.replicate declFvars.size false).toArray let mut bij := initBij
-- `usedFvars` keeps track of the fvars that were already used to match an mvar. for i in [:patterns.size] do
for i in [:declMvars.size] do let pattern := patterns[patterns.size - i - 1]!
let declMvar := declMvars[declMvars.size - i - 1]! if bij.forward.contains pattern.fvarId! then
let mut found := false continue
for j in [:declFvars.size] do for j in [:fvars.size] do
let declFvar := declFvars[declFvars.size - j - 1]! let fvar := fvars[fvars.size - j - 1]!
let usedFvar := usedFvars[declFvars.size - j - 1]! if bij.backward.contains fvar.fvarId! then
if ¬ usedFvar then
if ← isDefEq declMvar declFvar then
usedFvars := usedFvars.set! (declFvars.size - j - 1) true
found := true
break
else
continue continue
if ¬ found then return false
if let some bij' := matchExpr
(← instantiateMVars $ ← inferType pattern)
(← instantiateMVars $ ← inferType fvar) bij then
-- usedFvars := usedFvars.set! (fvars.size - j - 1) true
bij := bij'.insert pattern.fvarId! fvar.fvarId!
break
if ! bij.forward.contains pattern.fvarId! then return false
if strict then
return fvars.all (fun fvar => bij.backward.contains fvar.fvarId!)
return true return true
unsafe def evalHintMessageUnsafe : Expr → MetaM (Array Expr → MessageData) := unsafe def evalHintMessageUnsafe : Expr → MetaM (Array Expr → MessageData) :=
@ -62,19 +119,19 @@ def findHints (goal : MVarId) (doc : FileWorker.EditableDocument) : MetaM (Array
let some level ← getLevelByFileName? doc.meta.mkInputContext.fileName let some level ← getLevelByFileName? doc.meta.mkInputContext.fileName
| throwError "Level not found: {doc.meta.mkInputContext.fileName}" | throwError "Level not found: {doc.meta.mkInputContext.fileName}"
let hints ← level.hints.filterMapM fun hint => do let hints ← level.hints.filterMapM fun hint => do
let (declMvars, binderInfo, hintGoal) ← forallMetaBoundedTelescope hint.goal hint.intros openAbstractCtxResult hint.goal fun hintFVars hintGoal => do
-- TODO: Protect mvars in the type of `goal` to be instantiated? if let some fvarBij := matchExpr (← instantiateMVars $ hintGoal) (← instantiateMVars $ ← inferType $ mkMVar goal)
if ← isDefEq hintGoal (← inferType $ mkMVar goal)
then
let lctx ← getLCtx -- Local context of the `goal`
if ← matchDecls declMvars lctx.getFVars
then then
let text := (← evalHintMessage hint.text) declMvars let lctx := (← goal.getDecl).lctx
let ctx := {env := ← getEnv, mctx := ← getMCtx, lctx := ← getLCtx, opts := {}} if ← matchDecls hintFVars lctx.getFVars (strict := hint.strict) (initBij := fvarBij)
let text ← (MessageData.withContext ctx text).toString then
return some { text := text, hidden := hint.hidden } let text := (← evalHintMessage hint.text) hintFVars
else return none let ctx := {env := ← getEnv, mctx := ← getMCtx, lctx := ← getLCtx, opts := {}}
else return none let text ← (MessageData.withContext ctx text).toString
return some { text := text, hidden := hint.hidden }
else return none
else
return none
return hints return hints
open RequestM in open RequestM in

@ -0,0 +1,149 @@
/- Adapted from `Lean.Parser.StrInterpolation`
This version of interpolated strings deletes any initial spaces from
new lines. This keeps Markdown from interpreting indented material
as quotes.
-/
import Lean
namespace Lean.Parser
/-- Push `(Syntax.node tk <new-atom>)` onto syntax stack if parse was successful. -/
def mkNodeTokenNoWs (n : SyntaxNodeKind) (startPos : String.Pos) : ParserFn := fun c s => Id.run do
if s.hasError then
return s
let input := c.input
let stopPos := s.pos
let leading := mkEmptySubstringAt input startPos
let val := input.extract startPos stopPos
let wsStopPos := s.pos
let trailing := { str := input, startPos := stopPos, stopPos := wsStopPos : Substring }
let info := SourceInfo.original leading startPos trailing stopPos
s.pushSyntax (Syntax.mkLit n val info)
partial def interpolatedStrNoIndentFn (p : ParserFn) : ParserFn := fun c s =>
let input := c.input
let stackSize := s.stackSize
let rec parse (startPos : String.Pos) (c : ParserContext) (s : ParserState) : ParserState :=
let i := s.pos
if input.atEnd i then
let s := s.pushSyntax Syntax.missing
let s := s.mkNode interpolatedStrKind stackSize
s.setError "unterminated string literal"
else
let curr := input.get i
let s := s.setPos (input.next i)
if curr == '\"' then
let s := mkNodeToken interpolatedStrLitKind startPos c s
s.mkNode interpolatedStrKind stackSize
else if curr == '\n' then
-- Ignore initial spaces on a new line
let s := mkNodeTokenNoWs interpolatedStrLitKind startPos c s
let s := takeWhileFn (fun curr => curr == ' ') c s
parse s.pos c s
else if curr == '\\' then
andthenFn (quotedCharCoreFn isQuotableCharForStrInterpolant) (parse startPos) c s
else if curr == '{' then
let s := mkNodeToken interpolatedStrLitKind startPos c s
let s := p c s
if s.hasError then s
else
let i := s.pos
let curr := input.get i
if curr == '}' then
let s := s.setPos (input.next i)
parse i c s
else
let s := s.pushSyntax Syntax.missing
let s := s.mkNode interpolatedStrKind stackSize
s.setError "'}'"
else
parse startPos c s
let startPos := s.pos
if input.atEnd startPos then
s.mkEOIError
else
let curr := input.get s.pos;
if curr != '\"' then
s.mkError "interpolated string"
else
let s := s.next input startPos
parse startPos c s
@[inline] def interpolatedStrNoIndentNoAntiquot (p : Parser) : Parser := {
fn := interpolatedStrNoIndentFn (withoutPosition p).fn,
info := mkAtomicInfo "interpolatedStrNoIndent"
}
def interpolatedStrNoIndent : Parser :=
withAntiquot (mkAntiquot "interpolatedStrNoIndent" interpolatedStrKind) $ interpolatedStrNoIndentNoAntiquot termParser
open Lean.PrettyPrinter
open Lean.PrettyPrinter.Parenthesizer
open Lean.PrettyPrinter.Formatter
@[combinator_parenthesizer interpolatedStrNoIndent]
def interpolatedStrNoIndent.parenthesizer := interpolatedStr.parenthesizer
@[combinator_formatter interpolatedStrNoIndent]
def interpolatedStrNoIndent.formatter := interpolatedStr.formatter
end Lean.Parser
/- Adapted from Init/Meta -/
open Lean
private def decodeInterpStrQuotedChar (s : String) (i : String.Pos) : Option (Char × String.Pos) := do
match Syntax.decodeQuotedChar s i with
| some r => some r
| none =>
let c := s.get i
let i := s.next i
if c == '{' then pure ('{', i)
else none
private partial def decodeInterpStrLit (s : String) : Option String :=
let rec loop (i : String.Pos) (acc : String) : Option String :=
let c := s.get i
let i := s.next i
if c == '\"' || c == '{' then
pure acc
else if c == '\n' then
pure (acc.push c)
else if s.atEnd i then
pure acc
else if c == '\\' then do
let (c, i) ← decodeInterpStrQuotedChar s i
loop i (acc.push c)
else
loop i (acc.push c)
let c := s.get 0
if c == '\"' || c == '}' then
loop ⟨1⟩ ""
else
loop ⟨0⟩ ""
partial def isInterpolatedStrLit? (stx : Syntax) : Option String :=
match Syntax.isLit? interpolatedStrLitKind stx with
| none => none
| some val => decodeInterpStrLit val
open Elab
def expandInterpolatedStrChunks (chunks : Array Syntax) (mkAppend : Syntax → Syntax → TermElabM Syntax) (mkElem : Syntax → TermElabM Syntax) : TermElabM Syntax := do
let mut i := 0
let mut result := Syntax.missing
for elem in chunks do
let elem ← match isInterpolatedStrLit? elem with
| none => mkElem elem
| some str => mkElem (Syntax.mkStrLit str)
if i == 0 then
result := elem
else
result ← mkAppend result elem
i := i+1
return result
open TSyntax.Compat in
def expandInterpolatedStr (interpStr : TSyntax interpolatedStrKind) (type : Term) (toTypeFn : Term) : TermElabM Term := do
let r ← expandInterpolatedStrChunks interpStr.raw.getArgs (fun a b => `($a ++ $b)) (fun a => `($toTypeFn $a))
`(($r : $type))

@ -1 +1 @@
leanprover/lean4:nightly-2023-02-10 leanprover/lean4:nightly-2023-03-09

@ -6,7 +6,7 @@ import TestGame.Levels.Predicate
import TestGame.Levels.Contradiction import TestGame.Levels.Contradiction
import TestGame.Levels.Prime import TestGame.Levels.Prime
import TestGame.Levels.Sum import TestGame.Levels.Sum
import TestGame.Levels.Induction -- import TestGame.Levels.Induction
import TestGame.Levels.Numbers import TestGame.Levels.Numbers
import TestGame.Levels.Inequality import TestGame.Levels.Inequality
@ -34,8 +34,9 @@ Path Proposition → Implication → Predicate
Path Predicate → Contradiction → Sum → LeanStuff Path Predicate → Contradiction → Sum → LeanStuff
Path LeanStuff → SetTheory → SetTheory2 → SetFunction Path LeanStuff → SetTheory → SetTheory2 → SetFunction
Path Predicate → Prime → Induction Path Predicate → Prime -- → Induction
Path Sum → Inequality → Induction Path Sum → Inequality -- → Induction
Path Inequality → Function
Path SetTheory2 → Numbers Path SetTheory2 → Numbers
Path Module → Basis → Module2 Path Module → Basis → Module2

@ -75,40 +75,53 @@ Die Definition kann man mit `unfold odd at *` einsetzen.
DefinitionDoc Injective DefinitionDoc Injective
" "
`Injective f` ist als `Injective f` ist definiert als
``` ```
{a b : U}, a < b → f a < f b a b, f a = f b → a = b
``` ```
definiert. definiert.
" "
DefinitionDoc Surjective DefinitionDoc Surjective
" "
`Surjective f` ist definiert als
```
∀ a, (∃ b, f a = b)
```
" "
DefinitionDoc Bijective DefinitionDoc Bijective
" "
" "
DefinitionDoc LeftInverse
"
"
DefinitionDoc RightInverse
"
"
DefinitionDoc StrictMono DefinitionDoc StrictMono
" "
`StrictMono` `StrictMono f` ist definiert als
``` ```
∀ {a b : U}, f a f b → a = b a b, a < b → f a < f b
``` ```
" "
LemmaDoc not_odd as not_odd in "Nat" LemmaDoc even_iff_not_odd as even_iff_not_odd in "Nat"
"`¬ (odd n) ↔ even n`" "`Even n ↔ ¬ (Odd n)`"
LemmaDoc not_even as not_even in "Nat" LemmaDoc odd_iff_not_even as odd_iff_not_even in "Nat"
"`¬ (even n) ↔ odd n`" "`Odd n ↔ ¬ (Even n)`"
LemmaDoc even_square as even_square in "Nat" LemmaDoc even_square as even_square in "Nat"
"`∀ (n : ), even n → even (n ^ 2)`" "`∀ (n : ), Even n → Even (n ^ 2)`"
@ -178,3 +191,16 @@ LemmaDoc StrictMono.add as StrictMono.add in "Function"
LemmaDoc Odd.strictMono_pow as Odd.strictMono_pow in "Function" LemmaDoc Odd.strictMono_pow as Odd.strictMono_pow in "Function"
"" ""
LemmaDoc Exists.choose as Exists.choose in "Function"
""
LemmaDoc Exists.choose_spec as Exists.choose_spec in "Function"
""
LemmaDoc congrArg as congrArg in "Function"
""
LemmaDoc congrFun as congrFun in "Function"
""
LemmaDoc Iff.symm as Iff.symm in "Logic"
""

@ -8,3 +8,9 @@ import TestGame.Levels.Contradiction.L06_Summary
Game "TestGame" Game "TestGame"
World "Contradiction" World "Contradiction"
Title "Widerspruch" Title "Widerspruch"
Introduction "
Ihr begebt euch auf die Suche nach *Oddeus*. Nach etwas rumfragen, kommt ihr tatsächlich an
eine Dornenfestung und nachdem ihr erklärt habt, wer ihr seit, werdet ihr auf eine Audienz
gebeten.
"

@ -15,56 +15,46 @@ Title "Have"
Introduction Introduction
" "
Manchmal, wollen wir nicht am aktuellen Goal arbeiten, sondern zuerst ein **Oddeus**: Willkommen Reisende! Ich muss eingestehen ich bin in Gedanken noch
Zwischenresultat beweisen, welches wir dann benützen können. an etwas, könnt ihr mir bei diesem widersprüchlichen Problem helfen?
Mit `have [Name] : [Aussage]` kann man ein Zwischenresultat erstellen,
dass man anschliessen beweisen muss.
Wenn du zum Beispiel die Annahmen `(h : A → ¬ B)` und `(ha : A)` hast, kannst
du mit
```
have g : ¬ B
apply h
assumption
```
eine neue Annahme `(g : ¬ B)` erstellen. Danach beweist du zuerst diese Annahme,
bevor du dann mit dem Beweis forfährst.
" "
Statement -- Manchmal, wollen wir nicht am aktuellen Goal arbeiten, sondern zuerst ein
"Angenommen, man hat eine Implikation $A \\Rightarrow \\neg B$ und weiss, dass -- Zwischenresultat beweisen, welches wir dann benützen können.
$A \\land B$ wahr ist. Zeige, dass dies zu einem Widerspruch führt."
(A B : Prop) (h : A → ¬ B) (k : A ∧ B) : False := by
rcases k with ⟨h₁, h₂⟩
have h₃ : ¬ B
apply h
assumption
contradiction
Hint (A : Prop) (B : Prop) (h : A → ¬ B) (k : A ∧ B) : False => -- Mit `have [Name] : [Aussage]` kann man ein Zwischenresultat erstellen,
" Fang mal damit an, das UND in den Annahmen mit `rcases` aufzuteilen. -- dass man anschliessen beweisen muss.
"
Hint (A : Prop) (B : Prop) (h : A → ¬ B) (k : A) (f : B) : False => -- Wenn du zum Beispiel die Annahmen `(h : A → ¬ B)` und `(ha : A)` hast, kannst
" -- du mit
Auf Deutsch: \"Als Zwischenresultat haben wir `¬ B`.\" -- ```
-- have g : ¬ B
-- apply h
-- assumption
-- ```
-- eine neue Annahme `(g : ¬ B)` erstellen. Danach beweist du zuerst diese Annahme,
-- bevor du dann mit dem Beweis forfährst.
In Lean : Statement (A B : Prop) (h : A → ¬ B) (k : A ∧ B) : False := by
Hint "**Du**: Also als erstes Teile ich wohl mal das Und (`∧`) auf."
rcases k with ⟨h₁, h₂⟩
Hint "**Du**: Aber jetzt…
``` **Robo**: Du könntest dir ein passendes Zwischenresultat zurechtlegen, das dir hilft:
have k : ¬ B Mach mal `have g : ¬ B`!"
[Beweis von k] have g : ¬ B
``` · Hint "**Du**: Was und jetzt hab ich einfach angenommen das sei richtig?
"
-- example (n : ) : n.succ + 2 = n + 3 := by **Robo**: Ne, jetzt musst du das zuerst beweisen bevor du es dann benützen kannst."
-- ring_nf Hint (hidden := true) "**Robo**: `apply` scheint passend zu sein."
apply h
assumption
· Hint (hidden := true) "**Du**: Und wie war das nochmals wenn zwei Annahmen sich widersprechen?
Conclusion "" **Robo**: `contradiction`."
contradiction
NewTactics «have» NewTactic «have»
DisabledTactics «suffices» DisabledTactic «suffices»
NewDefinitions Even Odd Conclusion "*Oddeus*: Das stimmt wohl."
NewLemmas not_even not_odd

@ -15,52 +15,49 @@ Title "Suffices"
Introduction Introduction
" "
Die Taktik `suffices` funktioniert genau gleich wie `have`, *Oddeus*' Partner meldet sich.
vertauscht aber die beiden Beweisblöcke:
``` **Partner**: Ich habe letzthin was änliches gesehen, aber irgendwie verdreht.
suffices h : [Aussage] "
[Beweis des Goals (mithilfe von h)]
[Beweis der Aussage h]
```
Auf Deutsch entspricht `suffices g : [Aussage]` dem Ausdruck
\"Es genügt zu zeigen, dass `[Aussage]` wahr ist.\"
Man kann `have` und `suffices` nach belieben vertauschen. Bevorzugt, wählt man es so, -- Die Taktik `suffices` funktioniert genau gleich wie `have`,
dass der erste Beweisblock der kürzere ist. Zum Beispiel wäre bei der vorigen Aufgabe -- vertauscht aber die beiden Beweisblöcke:
`suffices` schöner gewesen:
" -- ```
-- suffices h : [Aussage]
-- [Beweis des Goals (mithilfe von h)]
-- [Beweis der Aussage h]
-- ```
-- Auf Deutsch entspricht `suffices g : [Aussage]` dem Ausdruck
-- \"Es genügt zu zeigen, dass `[Aussage]` wahr ist.\"
-- Man kann `have` und `suffices` nach belieben vertauschen. Bevorzugt, wählt man es so,
-- dass der erste Beweisblock der kürzere ist. Zum Beispiel wäre bei der vorigen Aufgabe
-- `suffices` schöner gewesen:
Statement Statement
"Angenommen, man hat eine Implikation $A \\Rightarrow \\neg B$ und weiss, dass "Angenommen, man hat eine Implikation $A \\Rightarrow \\neg B$ und weiss, dass
$A \\land B$ wahr ist. Zeige, dass dies zu einem Widerspruch führt." $A \\land B$ wahr ist. Zeige, dass dies zu einem Widerspruch führt."
(A B : Prop) (h : A → ¬ B) (k : A ∧ B) : False := by (A B : Prop) (h : A → ¬ B) (k₁ : A) (k₂ : B) : False := by
rcases k with ⟨h₁, h₂⟩ Hint "**Robo**: Ich weiss was er meint! Anstatt `have` kannst du auch `suffices`
suffices k : ¬ B brauchen. Das funktioniert genau gleich, aussert dass dann die beiden Goals vertauscht sind.
**Du**: Also nach `suffices g : ¬B` muss ich dann zuerst zeigen, wie man mit `g` den Beweis
abschliesst bevor ich `g` beweise?
**Robo**: Genau! Verwendende `have` und `suffices` einfach nach gutdünken."
suffices g : ¬ B
Hint "**Robo**: Also hier beendest du den Beweis angenommen {g} sei wahr."
contradiction contradiction
Hint "**Robo**: Und hier beweist du das Zwischenresultat."
apply h apply h
assumption assumption
Hint (A : Prop) (B : Prop) (h : A → ¬ B) (k : A ∧ B) : False => NewTactic «suffices»
" Fang mal damit an, das UND in den Annahmen mit `rcases` aufzuteilen. DisabledTactic «have»
"
Hint (A : Prop) (B : Prop) (h : A → ¬ B) (k : A) (f : B) : False =>
" Auf Deutsch: \"Es genügt `¬ B` zu zeigen, da dies zu einem direkten Widerspruch führt.\"
In Lean :
``` Conclusion "*Oddeus* nimmt den Brief, schaut ihn an und, rollt ihn zusammen.
suffices k : ¬ B
contradiction
[...]
```
"
Conclusion "" **Oddeus**: Ich verstehe meine Schwester nie. Kommt, vielleicht könnt ihr mir helfen.
NewTactics «suffices» Und er führt euch durch seinen Rosengarten."
DisabledTactics «have»
NewDefinitions Even Odd
NewLemmas not_even not_odd

@ -15,41 +15,51 @@ Level 3
Title "Per Widerspruch" Title "Per Widerspruch"
Introduction Introduction
" "**Oddeus**: Ich verstehe *Evenine* einfach nicht. Ich will euch auch gleich zeigen,
Eine sehr nützliche Beweismethode ist per Widerspruch. was sie mir letzthin geschrieben hat, aber zuerst schaut einmal unseren
wunderschönen Absurda-Tempel an!
Wir habe schon gesehen, dass `contradiction` einen Widerspruch in den Annahmen
sucht, und damit jegliches beweisen kann.
Um dorthin zu kommen, können wir `by_contra h` brauchen, welches das aktuelle Damit kommt ihr vor einen sehr hohen Turm, der ausschliesslich aus Dornenranken gewachsen
Goal auf `False` setzt und die Negierung des Goals als Annahme hinzufügt. scheint.
Insbesondere braucht man `by_contra h` meistens, wenn im Goal eine Negierung **Oddeus**: Versteht ihr, was hier über dem Eingang steht?
steht:
" "
Statement -- Eine sehr nützliche Beweismethode ist per Widerspruch.
"Angenommen $B$ ist falsch und es gilt $A \\Rightarrow B$. Zeige, dass $A$ falsch sein
muss."
(A B : Prop) (g : A → B) (b : ¬ B) : ¬ A := by
by_contra a
suffices b : B
contradiction
apply g
assumption
-- Wir habe schon gesehen, dass `contradiction` einen Widerspruch in den Annahmen
-- sucht, und damit jegliches beweisen kann.
HiddenHint (A : Prop) (B : Prop) (h : A → B) (b : ¬ B) : ¬ A => -- Um dorthin zu kommen, können wir `by_contra h` brauchen, welches das aktuelle
"`by_contra h` nimmt das Gegenteil des Goal als Annahme `(h : A)` und setzt das -- Goal auf `False` setzt und die Negierung des Goals als Annahme hinzufügt.
Goal auf `False`."
Hint (A : Prop) (B : Prop) (h : A → B) (b : ¬ B) (a : A) : False => -- Insbesondere braucht man `by_contra h` meistens, wenn im Goal eine Negierung
"Jetzt kannst du mit `suffices` oder `have` Fortschritt machen." -- steht:
HiddenHint (A : Prop) (B : Prop) (h : A → B) (b : ¬ B) (a : A) : False => Statement (A B : Prop) (g : A → B) (b : ¬ B) : ¬ A := by
"Zum Beispiel `suffices hb : B`." Hint "**Robo**: Ein `¬` im Goal heisst häufig, dass du einen Widerspruchsbeweis führen
möchtest.
**Du**: Und wie mach ich das? Mit `contradiction`?
**Robo**: Mit `by_contra h` fängst du einen an. Mit `contradiction` schliesst du ihn dann
später ab."
by_contra h
Hint "**Robo**: Jetzt hast du also eine Annahme `{h} : ¬ {A}`, und damit musst du einen
Widerspruch herbeileiten.
Ein Methode ist, dass du jetzt mit `suffices` sagts, zu was du denn gerne den Widerspruch
haben möchtest, zum Beispiel `suffices k : B`
"
suffices k : B
Hint "**Du**: Ah und jetzt kann ich einfach sagen dass sich die Anahmen `{B}` und `¬{B}`
widersprechen."
contradiction
Hint "**Robo**: Und jetzt kannst du noch das Ergebnis zeigen, das zu einem Widerspruch
geführt hat."
apply g
assumption
Conclusion "" NewTactic by_contra
NewTactics by_contra contradiction apply assumption Conclusion "**Oddeus**: Sehr gut, kommt mit hinein!"

@ -16,34 +16,36 @@ Title "Per Widerspruch"
Introduction Introduction
" "
Als Übung zu `by_contra` und dem bisher gelernten, zeige folgendes Lemma welches *Oddeus* Geht zu einem Regal mit vielen Dokumenten und beginnt zu suchen.
wir für die Kontraposition brauchen werden:
**Oddeus**: Ich hab's gleich. Hier ist eine Notiz meiner Gelehrter, die mir
mit solchen kryptischen Nachrichten helfen wollten.
Auf dem Pergament steht das ein Lemma mit dem Namen `not_imp_not`:
" "
Statement not_imp_not Statement not_imp_not (A B : Prop) : A → B ↔ (¬ B → ¬ A) := by
"$A \\Rightarrow B$ ist äquivalent zu $\\neg B \\Rightarrow \\neg A$." Hint "**Du**: Ich glaube, dafür kenn ich das meiste schon."
(A B : Prop) : A → B ↔ (¬ B → ¬ A) := by Hint (hidden := true) "**Robo**: Fang doch mal mit `constructor` an."
constructor constructor
intro h b intro h b
by_contra a by_contra a
Hint "**Robo**: Zur Erinnerung, hier würde ich mit `suffices g : B` einen Widerspruch
herbeiführen."
suffices b : B suffices b : B
contradiction contradiction
apply h apply h
assumption assumption
intro h a intro h a
Hint "**Robo**: Hier würde ich ebenfalls einen Widerspruch anfangen."
by_contra b by_contra b
Hint (hidden := true) "**Robo**: `suffices g : ¬ A` sieht nach einer guten Option aus."
suffices g : ¬ A suffices g : ¬ A
contradiction contradiction
apply h apply h
assumption assumption
-- TODO: Forbidden Tactics: apply, rw DisabledTactic rw
-- TODO: forbidden Lemma: not_not DisabledLemma not_not
HiddenHint (A : Prop) (B : Prop) : A → B ↔ (¬ B → ¬ A) =>
""
Conclusion ""
NewTactics contradiction constructor intro by_contra apply assumption Conclusion "**Du**: Und wie hilft uns das jetzt, was steht denn in dem Brief?"

@ -14,53 +14,57 @@ Title "Kontraposition"
Introduction Introduction
" "
Ein Beweis durch Kontraposition benützt im Grunde das eben bewiesene Lemma *Oddeus* reicht euch das Papier.
``` **Du**: Da steht etwas über `contrapose` hier…
lemma not_imp_not (A B : Prop) : (A → B) ↔ (¬ B → ¬ A) := by
[...]
```
Dazu gibt es die Taktik `contrapose`, welche eine Implikation im Goal **Oddeus**: Das ist doch klar eine Aggression uns gegenüber!
entsprechend umdreht.
Wir erinnern hier an die Taktik `revert h`, die aus der Annahme `h` eine Implikation **Robo**: Wartet mal, vielleicht wollte euch eure Schwester einfach von ihren neuen
im Goal erstellt. Endeckungen zeigen. Schaut, darunter ist eine Aufgabe.
"
Im Gegensatz dazu kann man auch einen Beweis durch Kontraposition führen. -- Ein Beweis durch Kontraposition benützt im Grunde das eben bewiesene Lemma
Das ist kein Widerspruch, sondern benützt dass `A → B` und `(¬ B) → (¬ A)`
logisch equivalent sind.
Wenn das Goal eine Implikation ist, kann man `contrapose` anwenden. -- ```
" -- lemma not_imp_not (A B : Prop) : (A → B) ↔ (¬ B → ¬ A) := by
-- [...]
-- ```
Statement -- Dazu gibt es die Taktik `contrapose`, welche eine Implikation im Goal
"Ist n² ungerade, so ist auch n ungerade. Beweise durch Kontraposition." -- entsprechend umdreht.
(n : ) (h : Odd (n ^ 2)): Odd n := by
revert h
contrapose
rw [not_odd]
rw [not_odd]
apply even_square
HiddenHint (n : ) (h : Odd (n ^ 2)) : Odd n => -- Wir erinnern hier an die Taktik `revert h`, die aus der Annahme `h` eine Implikation
"Um `contrapose` anzuwenden, brauchen wir eine Implikation `Odd (n ^ 2) → Odd n` im -- im Goal erstellt.
Goal. Benutze `revert h`!"
Hint (n : ) : Odd (n ^ 2) → Odd n => -- Im Gegensatz dazu kann man auch einen Beweis durch Kontraposition führen.
"Mit `contrapose` kann man die Implikation zu -- Das ist kein Widerspruch, sondern benützt dass `A → B` und `(¬ B) → (¬ A)`
`¬ (Not n) → ¬ (Odd n^2)` umkehren." -- logisch equivalent sind.
Hint (n : ) : ¬Odd n → ¬Odd (n ^ 2) => "Erinnere dich an das Lemma `not_odd`." -- Wenn das Goal eine Implikation ist, kann man `contrapose` anwenden.
open Nat
HiddenHint (n : ) : ¬Odd n → ¬Odd (n ^ 2) => "Dieses kann mit `rw` gebraucht werden." Statement (n : ) (h : Odd (n ^ 2)): Odd n := by
Hint "**Oddeus**: Wie soll das den gehen?
Hint (n : ) : Even n → ¬Odd (n ^ 2) => **Robo**: `contrapose` benutzt das Lemma eurer Gelehrter, `not_imp_not`. Also es wandelt
"rw [not_odd] muss hier zweimal angewendet werden." ein Goal `A → B` zu `¬B → ¬A ` um.
**Du**: Aber das Goal ist doch gar keine Implikation?
**Robo**: Mit `revert {h}` kannst du die Annahme `{h}` als Implikationsannahme ins Goal
schieben."
revert h
Hint "*Oddeus*: Ob man jetzt wohl dieses `contrapose` benutzen kann?"
contrapose
Hint (hidden := true) "**Du**: Warte mal, jetzt kann man wohl `even_iff_not_odd` verwenden…"
rw [← even_iff_not_odd]
rw [← even_iff_not_odd]
Hint "**Robo**: Und den Rest hast du bei *Evenine* als Lemma gezeigt!"
apply even_square
Hint (n : ) : Even n → Even (n ^ 2) => NewTactic contrapose
"Diese Aussage hast du bereits als Lemma bewiesen, schau mal in der Bibliothek." DisabledTactic by_contra
NewTactics contrapose rw apply Conclusion "**Oddeus**: Ah ich sehe, die Aussage ist, dass wir das nur zusammen lösen konnten.
NewDefinitions Even Odd Ich danke euch, darauf wäre ich nie gekommen."
NewLemmas not_even not_odd even_square

@ -14,63 +14,43 @@ Title "Contradiction"
Introduction Introduction
" "
**Du**: Sag mal Robo, das hätte ich aber auch als Widerspruch anstatt Kontraposition
beweisen können?
**Robo**: Klar. `contrapose` ist eine Kontraposition, `by_contra` ein Widerspruchsbeweis.
Probiers doch einfach!
In diesem Kapitel hast du also folgende Taktiken kennengelernt: In diesem Kapitel hast du also folgende Taktiken kennengelernt:
| | Taktik | Beispiel |
|:------|:----------------|:-------------------------------------------------------|
| 16 | `have` | Zwischenresultat annehmen. |
| 17 | `suffices` | Zwischenresultat annehmen. |
| 18 | `by_contra` | Widerspruch. (startet einen Widerspruch) |
| *3* | `contradiction` | *(Schliesst einen Widerspruchsbeweis)* |
| 19 | `contrapose` | Contraposition. |
| *9* | `revert` | Nützlich um danach `contrapose` anzuwenden. |
Als Vergleich zwischen Beweisen \"per Widerspruch\" Als Vergleich zwischen Beweisen \"per Widerspruch\"
und \"per Kontraposition\", beweise die Gleiche Aufgabe indem und \"per Kontraposition\", beweise die Gleiche Aufgabe indem
du mit `by_contra` einen Widerspruch suchst. du mit `by_contra` einen Widerspruch suchst.
" "
Statement open Nat
"Ist n² ungerade, so ist auch n ungerade. Beweise durch Widerspruch."
(n : ) (h : Odd (n ^ 2)) : Odd n := by Statement (n : ) (h : Odd (n ^ 2)) : Odd n := by
Hint "**Robo**: Fang diesmal mit `by_contra g` an!"
by_contra g by_contra g
Hint "**Robo**: Jetzt würde ich einen Widerspruch zu `Odd (n ^ 2)` führen."
Hint "**Robo**: Also `suffices g : ¬ Odd (n ^ 2)`."
suffices d : ¬ Odd (n ^ 2) suffices d : ¬ Odd (n ^ 2)
contradiction contradiction
rw [not_odd] at * rw [←even_iff_not_odd] at *
apply even_square apply even_square
assumption assumption
HiddenHint (n : ) (h : Odd (n^2)) : Odd n => DisabledTactic contrapose revert
"Schreibe `by_contra h₁` um einen Beweis durch Widerspruch zu starten."
Hint (n : ) (g : ¬ Odd n) (h : Odd (n^2)) : False =>
"
Am sinnvollsten ist es, hier einen Widerspruch zu `Odd (n^2)` zu suchen.
Dafür kannst du
```
suffices k : ¬ Odd (n ^ 2)
contradiction
```
benützen.
"
HiddenHint (n : ) (g : ¬ Odd (n^2)) (h : Odd (n^2)) : False =>
"Hier brauchst du nur `contradiction`."
Hint (n : ) (g : ¬ Odd n) (h : Odd (n^2)) : ¬ Odd (n^2) => Conclusion "**Robo**: Bravo! Hier nochmals ein Überblick, was wir jetzt alles auf diesem
"Das Zwischenresultat `¬Odd (n^2)` muss auch bewiesen werden. Mond gelernt haben:
Hier ist wieder das Lemma `not_Odd` hilfreich."
HiddenHint (n : ) (g : ¬ Odd n) (h : Odd (n^2)) : Even (n^2) => | | Taktik | Beispiel |
"Mit `rw [not_Odd] at *` kannst du im Goal und allen Annahmen gleichzeitig umschreiben." |:------|:----------------|:-------------------------------------------------------|
| 177 | `have` | Zwischenresultat annehmen. |
Hint (n: ) (h : Odd (n ^ 2)) (g : Even n) : Even (n ^ 2) => | 18 | `suffices` | Zwischenresultat annehmen. |
"Diese Aussage hast du bereits als Lemma bewiesen." | 19 | `by_contra` | Widerspruch. (startet einen Widerspruch) |
| *3* | `contradiction` | *(Schliesst einen Widerspruchsbeweis)* |
HiddenHint (n: ) (h : Odd (n ^ 2)) (g : Even n) : Even (n ^ 2) => | 20 | `contrapose` | Kontraposition. |
"Probiers mit `apply ...`" | *9* | `revert` | Nützlich um danach `contrapose` anzuwenden. |
"
NewTactics contradiction by_contra rw apply assumption -- TODO: suffices, have
NewDefinitions Even Odd
NewLemmas not_odd not_even even_square

@ -1,13 +1,24 @@
import TestGame.Levels.Function.L01_Function import TestGame.Levels.Function.L01_Function
import TestGame.Levels.Function.L02_Let import TestGame.Levels.Function.L02_Let
import TestGame.Levels.Function.L03_Composition import TestGame.Levels.Function.L03_Piecewise
import TestGame.Levels.Function.L06_Piecewise import TestGame.Levels.Function.L04_Injective
import TestGame.Levels.Function.L07_Injective import TestGame.Levels.Function.L05_Injective
import TestGame.Levels.Function.L07'_Injective import TestGame.Levels.Function.L06_Injective
import TestGame.Levels.Function.L08_Injective import TestGame.Levels.Function.L07_Surjective
import TestGame.Levels.Function.L09_Surjective import TestGame.Levels.Function.L08_Bijective
import TestGame.Levels.Function.L10_Bijective import TestGame.Levels.Function.L09_Inverse
import TestGame.Levels.Function.L11_Inverse
Game "TestGame" Game "TestGame"
World "Function" World "Function"
Title "Abbildungen" Title "Abbildungen"
Introduction "
Auf der Suche nach dem Buch der Urbilder landet ihr auf einem kleinen Mond, der bis auf
eine Insel komplett mit Wasser bedeckt zu sein scheint.
Auf der Insel seht ihr verschiedene große und kleine Behausungen, manche aus Stroh und Holz,
vereinzelte aus Lehm.
Planlos geht ihr zum ersten Haus bei dem jemand vorne außen sitzt.
"

@ -5,31 +5,59 @@ Game "TestGame"
World "Function" World "Function"
Level 1 Level 1
Title "" Title "Anonyme Funktionen"
Introduction Introduction
" "
Funktionen werden in Lean mit `` geschrieben (`\\to`), also dem gleichen Auf die Frage hin, ob sie von einem Bibliothek wisse, erzählt euch das kleine Mädchen,
Pfeil wie Implikationen. dass es auf der Insel nur einen gäbe, aber sie bedrängt euch so mit einer Frage,
dass sie euch gar nicht sagt, wo dieser zu finden sei.
"
Statement "" : ∃ f : , ∀ x, f x < x := by
use (fun x ↦ x - 1)
simp
Hint : ∃ f : , ∀ x, f x < x =>
"
**Robo**: `f : ` ist die Notation für eine Funktion und `f x` ist diese Funktion
angewendet auf ein Element `(x : )`.
**Du**: War `→` nicht eben noch eine Implikation?
**Robo**: Doch, die brauchen das gleiche Zeichen für beides.
Eine abstrakte Funktion ist ein entsprechendes Element `(f : )` oder `(g : )`. **Du**: Dann ist `f : ` also einfach abstrakt irgendeine Funktion,
Dies sagt aber gar nichts darüber, wie `f` tatsächlich definiert ist. wie definier ich aber jetzt konkret eine Abbildungsvorschrift?
Hat man eine Funktion `(f : A → B)` und ein Element `(x : A)` dann ist `f x` der **Robo**: Man kennt hier eine Notation für eine anonyme Funktion:
Bildpunkt in `B`. In Lean braucht man also keine Klammern um $f(x)$ zu schreiben. `fun (x : ) ↦ x ^ 2` ist
Eplizite, anonyme Funktionen kann man mit dem Syntax `fun (n : ) ↦ n ^ 2` definieren $$
(entweder `↦` (`\\map`) oder `=>` als Pfeil in der Mitte). \\begin\{aligned}
f : \\mathbb\{} &\\to \\mathbb\{} \\\\
x &\\mapsto x ^ 2
\\end\{aligned}
$$
**Robo**: PS, `↦` ist `\\mapsto`. Aber man kann auch stattdessen `=>` benützen.
" "
Statement HiddenHint : ∃ f : , ∀ x, f x < x =>
"Zeige, dass es eine Funktion $\\mathbb{N} \\to \\mathbb{Z}$ gibt, für die $f(x) < x$ gilt." "
: ∃ f : , ∀ x, f x < x := by **Du**: Ja aber was mach ich damit?
use (fun x ↦ x - 1)
simp
Hint : ∃ f : , ∀ x, f x < x => **Robo**: Wie immer gehst du ein `∃` mit `use …` an.
" "
Benütze eine anonyme Funktion `use (fun n ↦ _)` wobei `_` durch einen Ausdruck ersetzt
werden muss, so dass die Aussage erfüllt wird. -- TODO: Why does this not show?
HiddenHint : ∀ (x : ), (fun (y : ) => y - 1) x < x =>
"**Du**: Zu was sich das wohl vereinfacht?"
HiddenHint (x : ) : (fun y => y - 1) x < x =>
"**Du**: Zu was sich das wohl vereinfacht?"
Conclusion "Das Mädchen wird kurz ruhig, dann beginnt es zu lächeln und zeigt strahlend
in eine Richtung. Ihr folgt ihrem Finger und euch fällt in weiter ferne eine pompöse Struktur
auf einem flachen Hügel auf.
" "

@ -5,72 +5,78 @@ Game "TestGame"
World "Function" World "Function"
Level 2 Level 2
Title "" Title "let"
Introduction Introduction
" "
Ausserhalb eines Beweises kann man Funktionen mit `def` Ihr macht euch auf Richtung Bibliothek entlang kleiner Pfade zwischen verschiedenster Behausungen.
(anstatt `lemma`/`example`/`theorem`) definieren:
``` **Du**: Sag mal, ich weiss jetzt dass ich eine Funktion als `fun x ↦ x - 1` definieren kann,
def f (x : ) : := 1 / (1 + x^2) aber wie kann ich der einen Namen geben?
def f : := fun x ↦ 1 / (1 + x^2) **Robo**: Wenn jemand hier eine Funktion definiert, werden die dir
``` `def f (x : ) : := x - 1` oder `def f : := (fun x ↦ x - 1)` geben.
(die beiden Varianten sind äquivalent.) **Du**: Und das bedeutet beides das gleiche?
Um eine anonyme Funktion `fun x ↦ 1 / (1 + x^2)` **innerhalb** eines Beweis einem Namen **Robo**: Praktisch, ja. Aber! Wenn du eine Funktion in einer Aufgabe benennen willst,
zuzuordnen, benützt man `let`: schreibst du `let f := fun (x : ) ↦ x - 1`!
``` **Du**: Und was ist der Unterschied?
let f : := fun (n : ) ↦ n ^ 2
```
`def` und `let` funktionieren also fast gleich wie `lemma`/`example`/`theorem` und `have` mit **Robo**: Deines mit `let` ist für innerhalb von einem Beweis, das andere mit `def`
einem wichtigen Unterschied: ist für ausserhalb von einem Beweis. Hier, ich geb dir mal eine Aufgabe:
``` ```
have f : := fun (n : ) ↦ n ^ 2 def f (x : ) : := (x + 4)
let f₂ : := fun (n : ) ↦ n ^ 2
``` ```
und:
`have` vergisst sofort den \"Beweis\", das heisst, Lean weiss dann nur, dass es eine
Funktion `(f : )` gibt, aber nicht, wie diese definiert ist. `let` hingegen speichert
die Definition der Funktion.
Manchmal muss man Definitionen (von einem `def` oder `let` Statement) mit `unfold` einsetzen.
" "
def f (x : ) : := (x + 1) ^ 2 def f (x : ) : := (x + 4)
Statement
"
Given the function
```
def f (x : ) : := (x + 1) ^ 2
```
show that $f(x) = x^2 + 2x + 1$.
" Statement "" (x : ) : ∃ (g : ), (g ∘ f) x = x + 1 := by
: ∀ x, f x = x ^ 2 + 2 * x + 1 := by let g := fun (x : ) ↦ x - 3
intro x use g
simp
unfold f unfold f
ring ring
NewTactics «let» NewTactic «let»
OnlyTactics «let» intro unfold ring NewLemma Function.comp_apply
HiddenHint : ∀ x, f x = x ^ 2 + 2 * x + 1 => Hint (x : ) : ∃ g, (g ∘ f) x = x + 1 =>
"Fang zuerst wie immer mit `intro x` an." "**Du**: Ist `g ∘ f` Komposition von Funktionen?
Hint (x : ) : f x = x ^ 2 + 2 * x + 1 => **Robo**: Richtig! Das schreibt man mit `\\comp`.
"
Definitionen muss man manchmal manuell einsetzen um den Taktiken zu helfen.
Das macht man mit `unfold f` (oder alternativ mit `rw [f]`). **Du** Und hier könnte ich also zuerst
" `let g := fun (x : ) ↦ _` definieren, anstatt direkt
`use fun (x : ) ↦ _`?
HiddenHint (x : ) : f x = x ^ 2 + 2 * x + 1 => **Robo**: Genau! Das ist zwar praktisch das gleiche, aber kann manchmal nützlich sein.
" "
Nachdem die Definition von `f` eingesetzt ist, übernimmt `ring` den Rest"
-- TODO: Make some hints work here
Hint (x : ) : ((fun (x : ) ↦ x - 3) ∘ f) x = x + 1 =>
"**Robo**: Manchmal must du nachhelfen und Funktionen mit `unfold f` öffnen, manchmal nicht.
Um erlich zu sein, sagt mein Programm nicht genau wann man das machen muss…"
-- TODO : Make this work
Hint (x : ) (g := (fun (x : ) ↦ x - 3)) : (g ∘ f) x = x + 1 =>
"**Robo**: `(g ∘ f) x` ist per Definition `g (f x)`, aber mit
`rw [Function.comp_apply]` kann man das explizit umschreiben, aber `simp` kennt das
Lemma auch."
Hint (x : ) : f x - 3 = x + 1 =>
"**Robo**: Manchmal must du nachhelfen und Definitionen mit `unfold f` öffnen, mamchmal klappts
ohne.
Um erlich zu sein, sagt mein Programm nicht genau wann man das machen muss…"
-- TODO: Block simp-Lemma
Conclusion "**Du**: Dann verstehst du etwas Mathe?
**Robo**: Ich hatte ja keine Ahnung ob die generierte Aufgabe beweisbar ist…
Und damit erreicht ihr den Hügel mit der Bibliothek."

@ -1,22 +0,0 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 3
Title ""
Introduction
"
Komposition von Funktionen kann als `g ∘ f` geschrieben werden.
TODO
"
Statement
"TODO: Find an exercise."
(U T V : Type _) (f : U → V) (g : V → T) (x : U) : (g ∘ f) x = g (f x) := by
simp only [Function.comp_apply]
NewLemmas Function.comp_apply

@ -3,13 +3,14 @@ import Mathlib
Game "TestGame" Game "TestGame"
World "Function" World "Function"
Level 4 Level 3
Title "" Title "Komposition"
Introduction Introduction
" "
Du kommst an eine Tür mit zwei Wächtern. Der eine hat ein Schild Endlich kommt ihr zur Bibliothek. Komischerweise stehen an der Tür
zwei Wächtern. Der eine zeigt euch ein Blatt mit
``` ```
def f : := fun x ↦ 5 * x def f : := fun x ↦ 5 * x
@ -21,11 +22,9 @@ der andere eines mit
def g : := fun x ↦ if 0 ≤ x then 2*x else 0 def g : := fun x ↦ if 0 ≤ x then 2*x else 0
``` ```
Auf der Tür steht groß: und gibt Dir ein Blatt mit einer einzelnen Zeile am oberen Ende.
" "
example (P : Prop) [Decidable P] (a b : ) (h : P) : ite P a b = a := if_pos h
open Set open Set
namespace LevelFunction4 namespace LevelFunction4
@ -33,11 +32,6 @@ namespace LevelFunction4
def f : := fun x ↦ 5 * x def f : := fun x ↦ 5 * x
def g : := fun x ↦ if 0 ≤ x then 2*x else 0 def g : := fun x ↦ if 0 ≤ x then 2*x else 0
-- #eval (f2 + f2) 2
-- #check range f2
Statement "" Statement ""
: f ∘ g = g ∘ f := by : f ∘ g = g ∘ f := by
funext x funext x
@ -57,9 +51,9 @@ Statement ""
unfold f unfold f
ring ring
NewTactics funext by_cases simp_rw linarith NewTactic funext by_cases simp_rw linarith
NewLemmas not_le if_pos if_neg NewLemma not_le if_pos if_neg
Hint : f ∘ g = g ∘ f => Hint : f ∘ g = g ∘ f =>
@ -72,6 +66,7 @@ Hint : f ∘ g = g ∘ f =>
dann für dieses. dann für dieses.
" "
-- TODO : This does not trigger.
-- TODO: These 5 hints should be mutually exclusive. i.e. they should not trigger -- TODO: These 5 hints should be mutually exclusive. i.e. they should not trigger
-- if a assumption is missing. -- if a assumption is missing.
Hint (x : ) : (f ∘ g) x = (g ∘ f) x => Hint (x : ) : (f ∘ g) x = (g ∘ f) x =>
@ -92,12 +87,15 @@ könnte mit dem Lemma `not_le` zwischen `¬(0 ≤ {x})` und `0 < {x}` wechseln.
Hint (x : ) (h : 0 ≤ x) : f (g x) = g (f x) => Hint (x : ) (h : 0 ≤ x) : f (g x) = g (f x) =>
" "
**Robo**: Um das ausrechnen zu können, brauchst du nicht nur `0 ≤ x` sondern auch noch **Robo**: Um das ausrechnen zu können, brauchst du nicht nur `0 ≤ x` sondern auch noch
eine Annahme `0 ≤ f x`. eine neue Annahme `0 ≤ f x`.
**Du**: Also `have h₂ : _`?
" "
Hint (x : ) (h : 0 ≤ x) (h₂ : 0 ≤ f x) : f (g x) = g (f x) => Hint (x : ) (h : 0 ≤ x) (h₂ : 0 ≤ f x) : f (g x) = g (f x) =>
" "
**Du**: Mit den beiden Annahmen habe ich ja jetzt `(if 0 ≤ x then _)` wobei `0 ≤ x` wahr ist, **Du**: Mit den beiden Annahmen sagt die Definition von `g` ja z.B.
`(if 0 ≤ x then _)` wobei ich weiss dass `0 ≤ x` wahr ist,
kann ich das dann einfach vereinfachen? kann ich das dann einfach vereinfachen?
**Robo**: Dafür must du zuerst die Definition von `g` öffnen, also `unfold`. Und dann mit **Robo**: Dafür must du zuerst die Definition von `g` öffnen, also `unfold`. Und dann mit

@ -3,13 +3,13 @@ import Mathlib
Game "TestGame" Game "TestGame"
World "Function" World "Function"
Level 5 Level 4
Title "" Title ""
Introduction Introduction
" "
Ihr läuft durch verschiedenste Leere Gänge des Funktionentempels. Ihr läuft durch verschiedenste Gänge der Bibliothek, allesamt mit Büchern entlang der Wände.
**Du**: Wenn wir wüssten, dass nur ein möglicher Weg hierhin führt, könnten wir **Du**: Wenn wir wüssten, dass nur ein möglicher Weg hierhin führt, könnten wir
ausschliessen, dass wir im Kreis laufen. ausschliessen, dass wir im Kreis laufen.
@ -22,7 +22,7 @@ Statement "" : Injective (fun (n : ) ↦ n + 3) := by
intro a b intro a b
simp simp
NewDefinitions Injective NewDefinition Injective
Hint : Injective (fun (n : ) ↦ n + 3) => Hint : Injective (fun (n : ) ↦ n + 3) =>
"**Robo**: `Injective` ist als `∀ \{a b : U}, f a = f b → a = b` "**Robo**: `Injective` ist als `∀ \{a b : U}, f a = f b → a = b`

@ -5,7 +5,7 @@ set_option tactic.hygienic false
Game "TestGame" Game "TestGame"
World "Function" World "Function"
Level 6 Level 5
Title "" Title ""
@ -29,8 +29,8 @@ Statement "" : Injective (fun (n : ) ↦ n^3 + (n + 3)) := by
intro a b intro a b
simp simp
NewDefinitions Injective NewDefinition Injective
NewLemmas StrictMono.injective StrictMono.add Odd.strictMono_pow NewLemma StrictMono.injective StrictMono.add Odd.strictMono_pow
Hint : Injective fun (n : ) => n ^ 3 + (n + 3) => Hint : Injective fun (n : ) => n ^ 3 + (n + 3) =>
@ -76,4 +76,4 @@ Hint (a b : ) (h : a ^ 3 + (a + 3) = b ^ 3 + (b + 3)) : a = b =>
Conclusion "**Du**: Danke vielmals! Conclusion "**Du**: Danke vielmals!
Und ihr läst das Wesen mit im Gang stehen weiter über Injectivität nachdenkend." Und damit lässt das Wesen mitten im Gang stehen, wo es weiter über Injektivität nachdenkt."

@ -3,7 +3,7 @@ import Mathlib
Game "TestGame" Game "TestGame"
World "Function" World "Function"
Level 7 Level 6
Title "Injektive" Title "Injektive"
@ -13,13 +13,13 @@ Weiterirrend kommt ihr an eine Verzweigung.
**Robo**: Sieht beides gleich aus. **Robo**: Sieht beides gleich aus.
Ein paar Schritte in den linken Korridor hinein seht ihr gekritzel an der Wand: Ein paar Schritte in den linken Korridor hinein seht ihr auf dem Boden ein Blatt mit Gekritzel:
``` ```
def f : := fun n ↦ if Even n then n^2 else n+1 def f : := fun n ↦ if Even n then n^2 else n+1
``` ```
**Du**: Hier haben wir eine stückweis definierte Funktion **Du**: Hier haben wir wieder eine stückweise Funktion
$$ $$
f(n) = \\begin{cases} f(n) = \\begin{cases}

@ -0,0 +1,37 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 7
Title "Surjektive"
Introduction
"
Endlich kommt ihr in einen große, beleuchteten zentralen Raum.
Alle Wände sind voll mit Büchern und
in der Mitte sitzt an einem einsamen
Tisch ein Gelehrter, der tatsächlich das gesuchte Buch zeigen kann.
Bevor er dieses aushändigt, will er aber folgendes wissen:
"
open Function
Statement "" : Surjective (fun (n : ) ↦ n + 1) := by
intro y
use y-1
simp
NewDefinition Surjective
Hint : Surjective (fun (n : ) ↦ n + 1) =>
"**Robo**: Die Definition von `Surjective f` ist `∀ y, (∃ x, f x = y)`.
**Du**: Dann kann ich das auch einfach wie Quantifier behandeln?
**Robo**: Schieß drauf los!"
Conclusion
"Der Gelehrte händigt euch schmunzelnd das Buch aus."

@ -0,0 +1,38 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 8
Title ""
Introduction
"
**Du**: Ehm, und wie kommen wir da wieder raus?
**Gelehrter**: Gerne zeige ich euch den Weg, nachdem ihr mir auch noch folgendes erklärt:
"
open Function
Statement "" : Bijective (fun (n : ) ↦ n + 1) := by
unfold Bijective
constructor
intro a b
simp
intro y
use y-1
simp
Hint : Bijective (fun (n : ) ↦ n + 1) =>
"**Robo** *(flüsternd)*: `Bijectve f` ist als `Injective f ∧ Surjective f` definiert.
**Du**: Dann ist das ja ganz simpel!
"
Conclusion
"Zufrieden drückt euch der Gelehrte eine neue Fackel in die Hand und
zeigt euch den Weg nach draussen."
NewDefinition Bijective

@ -0,0 +1,61 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 9
Title "Inverse"
Introduction
"
Eigentlich hast du nur beiläufig Robo gefragt, ob bijektiv nicht auch bedeute, dass
eine Inverse Funktion bestehe. Jetzt steht ihr aber schon seit einer halben Stunde rum
und der Gelehrte möchte wissen, wie das den genau ginge.
Offensichtlich kennt er diese Aussage als `Function.bijective_iff_has_inverse` aus seinen Büchern,
aber er möchte, dass Du ihm das hier und jetzt nochmals von Grund auf zeigst.
"
open Function
--TODO: This is a really hard proof
Statement bijective_iff_has_inverse "" {A B : Type} (f : A → B) :
Bijective f ↔ ∃ g, LeftInverse g f ∧ RightInverse g f := by
constructor
intro h
use fun x => (h.2 x).choose
constructor
· intro x
simp
apply h.1
apply Exists.choose_spec (h.2 (f x))
· intro x
simp
apply Exists.choose_spec (h.2 x)
intro ⟨g, h₁, h₂⟩
constructor
· intro a b hab
have h : g (f a) = g (f b)
· apply congrArg
assumption
rw [h₁, h₁] at h
assumption
· intro x
use g x
rw [h₂]
NewDefinition LeftInverse RightInverse
NewLemma Exists.choose Exists.choose_spec congrArg congrFun
DisabledLemma Function.bijective_iff_has_inverse
Hint {A B : Type} (f : A → B) : Bijective f ↔ ∃ g, LeftInverse g f ∧ RightInverse g f =>
"**Du**: Nah da sagt mir so manches nichts, aber ich kann ja mal mit dem `↔` anfangen, das kenn
ich ja schon."
Conclusion
"Endlich entkommt ihr der Bibliothek.
**Robo**: Da würden mich keine zehn Pferde nochmals hineinbringen!
**Du**: Von wegen Pferden, wie viele PS hat eigentlich unser Raumschiff?"

@ -1,19 +0,0 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 8
Title "Surjektive"
Introduction
"
"
Statement
""
: (fun (n : ) ↦ n + 1).Surjective := by
intro y
use y-1
simp

@ -1,23 +0,0 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 9
Title ""
Introduction
"
"
Statement
""
: (fun (n : ) ↦ n + 1).Bijective := by
constructor
intro a b hab
simp at hab
assumption
intro y
use y-1
simp

@ -0,0 +1,93 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 10
Title "Inverse"
Introduction
"
Eigentlich hast du nur beiläufig Robo gefragt, ob bijektiv nicht auch bedeute, dass
eine Inverse Funktion bestehe. Jetzt steht ihr aber schon seit einer halben Stunde rum
und der Gelehrte möchte wissen, wie das den genau ginge.
Offensichtlich kennt er diese Aussage als `Function.bijective_iff_has_inverse` aus seinen Büchern,
aber er möchte, dass Du ihm das hier und jetzt nochmals von Grund auf zeigst.
"
open Function
set_option pp.rawOnError true
Statement bijective_iff_has_inverse "" {A B : Type} (f : A → B) :
Bijective f ↔ ∃ g, LeftInverse g f ∧ RightInverse g f := by
Branch
exfalso
Hint "Das war eine blöde Idee
dd
ddds
"
Hint (hidden := true) "constructor"
constructor
Hint "intro h" -- does not show
intro h
Hint "rcases h with ⟨h₁, h₂⟩" -- shows too late: 1, 2 after
rcases h with ⟨h₁, h₂⟩
Hint (strict := true) "let g := fun x => (h₂ x).choose" -- shows correct + 1 after
let g := fun x => (h₂ x).choose
Hint "use g"
use g
Hint "constructor" -- does not show
constructor
Hint "intro x" -- does not show
intro x
Hint "simp" -- Error updating: Error fetching goals: Rpc error: InternalError: unknown universe metavariable '?_uniq.286465'. Try again.
simp
Hint "apply h₁"
apply h₁
Hint "apply Exists.choose_spec (h₂ (f x))"
apply Exists.choose_spec (h₂ (f x))
Hint "intro y" -- Error updating: Error fetching goals: Rpc error: InternalError: unknown universe metavariable '?_uniq.286465'. Try again.
intro y
Hint "simp"
simp
Hint "apply Exists.choose_spec (h₂ y)"
apply Exists.choose_spec (h₂ y)
Hint "intro ⟨g, h₁, h₂⟩"
intro ⟨g, h₁, h₂⟩
Hint "constructor"
constructor
Hint "intro a b hab"
intro a b hab
Hint (strict := true) "have h : g (f a) = g (f b)"
have h : g (f a) = g (f b)
Hint "apply congrArg"
apply congrArg
Hint "assumption"
assumption
Hint "rw [h₁, h₁] at h"
rw [h₁, h₁] at h
Hint "assumption"
assumption
Hint "intro x"
intro x
Hint "use g x"
use g x
Hint "rw [h₂]"
rw [h₂]
-- NewDefinition LeftInverse RightInverse
-- NewLemma Exists.choose Exists.choose_spec congrArg congrFun
-- DisabledLemma Function.bijective_iff_has_inverse
Hint (A B: Type) (f : A → B) (h : Bijective f) :
(∃ g, LeftInverse g f ∧ RightInverse g f) → Bijective f =>
"Test"
Conclusion
"Endlich entkommt ihr dem Tempel.
**Robo**: Da würden mich keine zehn Pferde nochmals hineinbringen!
**Du**: Von wegen Pferden, wie viele PS hat eigentlich unser Raumschiff?"

@ -8,8 +8,9 @@ import TestGame.Levels.Implication.L07_Rw
import TestGame.Levels.Implication.L08_Iff import TestGame.Levels.Implication.L08_Iff
import TestGame.Levels.Implication.L09_Iff import TestGame.Levels.Implication.L09_Iff
import TestGame.Levels.Implication.L10_Apply import TestGame.Levels.Implication.L10_Apply
import TestGame.Levels.Implication.L11_Rw import TestGame.Levels.Implication.L11_ByCases
import TestGame.Levels.Implication.L12_Summary import TestGame.Levels.Implication.L12_Rw
import TestGame.Levels.Implication.L13_Summary
Game "TestGame" Game "TestGame"
World "Implication" World "Implication"
@ -21,10 +22,13 @@ Zurück im Raumschiff macht ihr euch auf den Weg zum einem der beiden Monde, die
beide bewohnt zu sein scheinen. beide bewohnt zu sein scheinen.
**Du**: Sag mal Robo, Königin *Logisindes* hat under anderem von Implikationen gesprochen, **Du**: Sag mal Robo, Königin *Logisindes* hat under anderem von Implikationen gesprochen,
aber niemand von den Einwohnern... aber niemand von den Einwohnern wusste was davon...
**Robo**: Auf dem Mond *Implis* den wir gerade ansteuern können sie uns vielleicht mehr **Robo**: Auf dem Mond *Implis* den wir gerade ansteuern können sie uns vielleicht mehr
erzählen… erzählen…
Und damit leitet Robo den Landeanflug ein. Und damit leitet Robo den Landeanflug ein. Implis scheint ein riesiger Tagbau zu sein auf
dem nach allem möglichen gegraben wird. Überall seht ihr Förderbänder kreuz und queer.
Das Operationsteam begrüsst euch freundlich und lädt zum Essen im Kommandoturm.
" "

@ -1,4 +1,5 @@
import TestGame.Metadata import TestGame.Metadata
import Mathlib.Tactic.Tauto
set_option tactic.hygienic false set_option tactic.hygienic false
@ -10,31 +11,23 @@ Title "Intro"
Introduction Introduction
" "
## Implikationen **Operationsleiter**: Sagt mal, könnt ihr mir hier weiterhelfen?
In diesem Kapitel lernst du Implikation ($\\Rightarrow$) und Genau-dann-wenn
($\\Leftrightarrow$) kennen.
Dazu lernst du, wie man bereits bewiesene Sätze verwendet.
Seien `(A B : Prop)` zwei logische Aussagen. Eine Implikation $A \\Rightarrow B$ schreibt
man in Lean als `A → B` (`\\to`).
Wenn das Goal eine Implikation $A \\Rightarrow B$ ist, kann man mit
`intro ha` annehmen, dass $A$ wahr ist. Dann muss man $B$ beweisen.
" "
Statement Statement (A B : Prop) (hb : B) : A → (A ∧ B) := by
"Wenn $B$ wahr ist, dann ist die Implikation $A \\Rightarrow (A ∧ B)$ wahr." Hint "**Du**: Einen Moment, das ist eine Implikation (`\\to`),
(A B : Prop) (hb : B) : A → (A ∧ B) := by also `A` impliziert `A und B`, soweit so gut, also eine Tautologie.
**Robo**: Die scheinen hier `tauto` auch nicht zu verstehen.
Implikationen kannst du aber mit `intro h` angehen."
intro hA intro hA
Hint "**Du**: Jetzt habe ich also angenommen, dass `A` wahr ist und muss `A ∧ B` zeigen,
das kennen wir ja schon."
constructor constructor
assumption assumption
assumption assumption
HiddenHint (A : Prop) (B : Prop) (hb : B) : A → (A ∧ B) => Conclusion "Der Operationsleiter nickt bedacht."
"Mit `intro ha` kann man annehmen, dass $A$ wahr ist. danach muss man $A \\land B$ zeigen."
Hint (A : Prop) (B : Prop) (ha : A) (hb : B) : (A ∧ B) =>
"Jetzt kannst du die Taktiken aus dem letzten Kapitel verwenden."
NewTactics intro constructor assumption NewTactic intro
DisabledTactic tauto

@ -9,34 +9,22 @@ Level 2
Title "Revert" Title "Revert"
Introduction Introduction
" "Jemand aus der Gruppe gibt dir ein Blatt Papier mit einer Zeile Text:"
Mit `intro` kann man also eine Implikation aus dem Goal entfernen, indem man
die Implikationsprämisse zu den *Annahmen* hinzufügt: Statement (A B : Prop) (ha : A) (h : A → B) : B := by
Hint "**Robo**: Mit `revert {ha}` kann man die Annahme `ha` als
``` Implikationsprämisse vorne ans Goal anhängen, dann ist das Goal `{A} → {B}`.
example : A → B :=
[Beweis] **Du**: Das wirkt etwas unnatürlich.
```
**Robo**: Schon, ja. Aber als Tool kann das manchmal nützlich sein."
wird zu
```
example (ha : A) : B :=
[Beweis]
```
Seltener kann auch die andere Richtung nützlich sein. Mit `revert ha` kann man die Annahme
`ha` entfernen und als Implikationsprämisse vor's Goal hängen.
"
Statement
"Angenommen $A$ ist eine wahre Aussage und man hat eine Implikation $A \\Rightarrow B$, zeige
dass $B$ wahr ist."
(A B : Prop) (ha : A) (h : A → B) : B := by
revert ha revert ha
assumption assumption
HiddenHint (A : Prop) (B : Prop) (ha : A) (h : A → B): B => Conclusion "**Du**: Aber das müsste doch auch anders gehen, ich hätte jetzt intuitiv
"Mit `revert ha` kann man die Annahme `ha` als Implikationsprämisse vorne ans Goal anhängen." die Implikation $A \\Rightarrow B$ angewendet und behauptet, dass es genügt $A$ zu zeigen…
Daraufhin lächelt der Fragende nur vorahnend."
NewTactics revert assumption NewTactic revert
DisabledTactic tauto

@ -9,6 +9,8 @@ Title "Apply"
Introduction Introduction
" "
Sein Kollege zieht eine Linie unter deinen Beweis, schreibt ein durchgestrichenes ~`revert`~
hin und gibt dir das Blatt wieder.
`revert` ist aber nur selten der richtige Weg. `revert` ist aber nur selten der richtige Weg.
Im vorigen Beispiel würde man besser die Implikation $A \\Rightarrow B$ *anwenden*, also Im vorigen Beispiel würde man besser die Implikation $A \\Rightarrow B$ *anwenden*, also
@ -18,21 +20,20 @@ Wenn man eine Implikation `(g : A → B)` in den Annahmen hat, bei welcher die K
(also $B$) mit dem Goal übereinstimmt, kann man `apply g` genau dies machen. (also $B$) mit dem Goal übereinstimmt, kann man `apply g` genau dies machen.
" "
Statement Statement (A B : Prop) (hA : A) (h : A → B) : B := by
"Seien $A, B$ logische Aussagen, wobei $A$ wahr ist und $A \\Rightarrow B$. Hint "**Robo**: Du hast natürlich recht, normalerweise ist es viel schöner mit
Zeige, dass $B$ wahr ist." `apply {h}` die Implikation anzuwenden."
(A B : Prop) (hA : A) (g : A → B) : B := by apply h
apply g Hint "**Du**: Und jetzt genügt es also `A` zu zeigen."
assumption assumption
HiddenHint (A : Prop) (B : Prop) (hA : A) (g : A → B) : B => Conclusion "**Robo** Übrigens mit `apply LEMMA` kannst auch jedes Lemma anwenden, dessen
"Mit `apply g` kannst du die Implikation `g` anwenden." Aussage mit dem Goal übereinstimmt.
HiddenHint (A : Prop) (B : Prop) (hA : A) (g : A → B) : A => Die beiden Fragenden schauen das Blatt an und murmeln zustimmend."
"Nachdem du die Implikation `A → B` angewendet hast, musst du nur noch $A$ zeigen,
dafür hast du bereits einen Beweis in den Annahmen."
NewTactics apply assumption NewTactic apply
DisabledTactic revert tauto
-- Katex notes -- Katex notes
-- `\\( \\)` or `$ $` for inline -- `\\( \\)` or `$ $` for inline

@ -10,26 +10,29 @@ Title "Implikation"
Introduction Introduction
" "
Hier eine Übung zu Implikationen. **Du** *(zu Robo)*: Testen die uns eigentlich hier?
Fast immer ist es der richtige Weg, wenn du mit `intro` anfängst.
Ein älteres Gruppenmitglied schiebt ein Tablet über den Tisch und beginnt in leiser
Stimme zu erklären.
**Mitarbeiterin**: Eines unserer Kontrollelemente ist kaputt und ist verwirrt, wo Sachen hinkommen.
Gesteuert werden diese über Panels, und hier hab ich das Übungspanel, mit dem wir neue
Ingeneure ausbilden:
" "
Statement Statement (A B C : Prop) (f : A → B) (g : B → C) : A → C := by
"Angenommen man weiss $A \\Rightarrow B \\Rightarrow C$, zeige dass $A \\Rightarrow C$." Hint "**Du**: Ich soll Implikationen $A \\Rightarrow B \\Rightarrow C$ zu $A \\Rightarrow C$
(A B C : Prop) (f : A → B) (g : B → C) : A → C := by kombinieren?
**Robo**: Am besten fängst du mit `intro` an und arbeitest dich dann rückwärts durch."
intro hA intro hA
Hint (hidden := true) "**Robo**: Das ist wieder eine Anwendung von `apply`."
apply g apply g
apply f apply f
assumption assumption
HiddenHint (A : Prop) (B : Prop) (C : Prop) (f : A → B) (g : B → C) : A → C => Conclusion "**Du**: Ich hab das Konzept verstanden.
"Mit `intro hA` kann man annehmen, dass $A$ wahr ist. danach muss man $B$ zeigen."
Hint (A : Prop) (B : Prop) (C : Prop) (hA : A) (f : A → B) (g : B → C) : C =>
"Jetzt ist es ein altbekanntes Spiel von `apply`-Anwendungen."
HiddenHint (A : Prop) (B : Prop) (C : Prop) (hA : A) (f : A → B) (g : B → C) : C => Die Mitarbeiterin ist zufrieden und wünscht euch Glück auf der Mission."
"Du willst $C$ beweisen. Suche also nach einer Implikation $\\ldots \\Rightarrow C$ und wende
diese mit `apply` an."
NewTactics intro apply assumption revert DisabledTactic tauto

@ -8,46 +8,46 @@ Title "Implikation"
Introduction Introduction
" "
Noch eine Übung: Angenommen man hat folgende Implikationen: Selbstsicher folgt ihr den Anweisungen und geht nach draussen zum
defekten Kontrollelement. Dieses zeigt ein kompliziertes Diagram:
$$ $$
\\begin{CD} \\begin{CD}
A @>{f}>> B @<{g}<< C \\\\ A @>{f}>> B @<{g}<< C \\\\
@. @V{h}VV @V{m}VV \\\\ @. @V{h}VV @V{m}VV \\\\
D @>{i}>> E @>{k}>> F D @>{i}>> E @>{k}>> F \\\\
@A{m}AA @A{n}AA @V{p}VV \\\\
G @<{q}<< H @>{r}>> I
\\end{CD} \\end{CD}
$$ $$
" "
Statement Statement
"Beweise $A \\Rightarrow F$." (A B C D E F G H I : Prop)
(A B C D E F : Prop) (f : A → B) (g : C → B) (h : B → E) (f : A → B) (g : C → B) (h : B → E) (i : D → E) (k : E → F) (m : G → D)
(i : D → E) (k : E → F) (m : C → F) : A → F := by (n : H → E) (p : F → I) (q : H → G) (r : H → I) : A → I := by
Hint "**Du**: Also ich muss einen Pfad von Implikationen $A \\Rightarrow I$ finden.
**Robo**: Und dann fängst du am besten wieder mit `intro` an."
intro ha intro ha
Branch
apply r
Hint "**Robo**: Das sieht nach einer Sackgasse aus…"
Hint (hidden := true) "**Robo**: Na wieder `apply`, was sonst."
apply p
apply k apply k
apply h apply h
Branch
apply g
Hint "**Robo**: Nah, da stimmt doch was nicht…"
apply f apply f
assumption assumption
HiddenHint (A : Prop) (B : Prop) (C : Prop) (D : Prop) (E : Prop) (F : Prop) Conclusion
(f : A → B) (g : C → B) (h : B → E) "Mit einem lauten Ratteln springen die Förderbänder wieder an.
(i : D → E) (k : E → F) (m : C → F) : A → F =>
"Wieder ist es schlau, mit `intro` anzufangen."
HiddenHint (A : Prop) (B : Prop) (C : Prop) (D : Prop) (E : Prop) (F : Prop)
(hA : A) (f : A → B) (g : C → B) (h : B → E)
(i : D → E) (k : E → F) (m : C → F) : F =>
"Versuch mit `apply` den richtigen Weg zu finden."
Hint (A : Prop) (B : Prop) (C : Prop) (D : Prop) (E : Prop) (F : Prop)
(hA : A) (f : A → B) (g : C → B) (h : B → E)
(i : D → E) (k : E → F) (m : C → F) : C =>
"Sackgasse. Probier doch einen anderen Weg."
Hint (A : Prop) (B : Prop) (C : Prop) (D : Prop) (E : Prop) (F : Prop) **Operationsleiter**: Vielen Dank euch! Kommt ich geb euch eine Führung und stell euch den
(hA : A) (f : A → B) (g : C → B) (h : B → E) Technikern hier vor."
(i : D → E) (k : E → F) (m : C → F) : D =>
"Sackgasse. Probier doch einen anderen Weg."
NewTactics apply assumption revert intro DisabledTactic tauto
-- https://www.jmilne.org/not/Mamscd.pdf -- https://www.jmilne.org/not/Mamscd.pdf

@ -8,36 +8,39 @@ Title "Genau dann wenn"
Introduction Introduction
" "
Genau-dann-wenn, $A \\Leftrightarrow B$, wird als `A ↔ B` (`\\iff`) geschrieben. Als erstes kommt ihr in einen kleinen Raum mit ganz vielen Bildschirmen.
`A ↔ B` ist eine Struktur (ähnlich wie das logische UND), die aus zwei Teilen besteht:
- `mp`: die Implikation $A \\Rightarrow B$. Ein junges Wesen dreht sich auf dem Stuhl um, und sagt:
- `mpr`: die Implikation $B \\Rightarrow A$.
Hat man ein $\\Leftrightarrow$ im Goal, nimmt man dieses ebenfalls mit der Taktik **Mitarbeiter**: Oh hallo! Schaut euch mal das hier an!
`constructor` auseinander und zeigt dann beide Richtungen einzeln.
" "
Statement Statement (A B : Prop) (mp : A → B) (mpr : B → A) : A ↔ B := by
"Zeige dass `B ↔ C`." Hint "**Robo**: Das ist ein genau-dann-wenn Pfeil: `\\iff`. Er besteht aus zwei Teilen:
(A B : Prop) (mp : A → B) (mpr : B → A) : A ↔ B := by `A ↔ B` ist als `⟨A → B, B → A⟩` definiert.
**Du**: Also ganz ähnlich wie das UND, `A ∧ B`?
**Robo**: Genau. Entsprechend kannst du hier auch mit `constructor` anfangen."
constructor constructor
Hint "**Du**: Ah und die beiden hab ich schon in den Annahmen."
assumption assumption
assumption assumption
HiddenHint (A : Prop) (B : Prop) : A ↔ B =>
"Eine Struktur wie `A ↔ B` kann man mit `constructor` zerlegen."
HiddenHint (A : Prop) (B : Prop) (h : A → B) : A → B =>
"Such mal in den Annahmen."
Conclusion Conclusion
" "
Die Taktik `constructor` heisst so, weil `↔` als \"Struktur\" definiert ist, die **Robo**: Übrigens, bei `(h : A ∧ B)` haben die beiden Teile `h.left` und `h.right` geheissen,
aus mehreren Einzelteilen besteht: `⟨A → B, B → A⟩`. Man sagt also Lean, es soll versuchen, hier bei `(h : A ↔ B)` heissen sie `h.mp` und `h.mpr`.
ob das Goal aus solchen Einzelteilen \"konstruiert\" werden kann.
**Du**: Also `h.mp` ist `A → B`? Wieso `mp`?
**Operationsleiter**: \"Modulo Ponens\" ist ein lokaler Begriff hier,
aber das ist doch nicht wichtig.
**Robo**: Und das \"r\" in `mpr` stünde für \"reverse\" weils die Rückrichtung ist.
" "
NewTactics constructor assumption NewTactic constructor
DisabledTactic tauto rw
-- TODO : `case mpr =>` ist mathematisch noch sinnvoll. -- TODO : `case mpr =>` ist mathematisch noch sinnvoll.

@ -11,64 +11,35 @@ Title "Genau dann wenn"
Introduction Introduction
" "
Hat man ein `(h : A ↔ B)` in den Annahmen, hat man die gleichen beiden Optionen wie beim Als nächstes begenet ihr jemandem im Flur.
logischen UND plus noch eine neue:
1. Mit `h.mp` und `h.mpr` (oder `h.1` und `h.2`) kann man die einzelnen Implikationen Dieser hat schon von euch gehört und will sofort wissen, ob ihr ihm helfen könnt:
direkt auswählen.
2. Mit `rcases h with ⟨h₁, h₂⟩` könnte man die Struktur `h` zerlegen und man erhält zwei
separate Annahmen `(h₁ : A → B)` und `(h₂ : B → A)`
3. **Mit** `rw [h]` **kann man im Goal `A` durch `B` ersetzen.**
Wir widmen uns zuerst `rw`. Dies steht für \"rewrite\". Da $A$ und $B$ logisch äquivalent
sind, kann man beliebig das eine mit dem anderen vertauschen.
`rw [h]` ersetzt $A$ durch $B$.
Dabei gibt es noch einige Tricks:
- `rw [← h]` ersetzt umgekehrt $B$ durch $A$ (`\\l`, kleines L).
- `rw [h, g]` ist das gleiche wie `rw [h]` gefolgt von `rw [g]`.
" "
Statement Statement (A B C D : Prop) (h₁ : C ↔ D) (h₂ : A ↔ B) (h₃ : A ↔ D) : B ↔ C := by
"Zeige dass `B ↔ C`." Hint "**Du**: $B \\iff A \\iff D \\iff C$, die sind doch alle äquivalent…
(A B C D : Prop) (h₁ : C ↔ D) (h₂ : A ↔ B) (h₃ : A ↔ D) : B ↔ C := by
rw [h₁]
rw [←h₂]
assumption
HiddenHint (A : Prop) (B : Prop) (C : Prop) (D : Prop) (h₁ : C ↔ D) (h₂ : A ↔ B) (h₃ : A ↔ D) : B ↔ C =>
"Im Goal kommt `C` vor und `h₁` sagt `C ↔ D`.
Probiers doch mit `rw [h₁]`."
HiddenHint (A : Prop) (B : Prop) (C : Prop) (D : Prop) (h₁ : C ↔ D) (h₂ : A ↔ B) (h₃ : A ↔ D) : A ↔ C =>
"Im Goal kommt `C` vor und `h₁` sagt `C ↔ D`.
Probiers doch mit `rw [h₁]`."
Hint (A : Prop) (B : Prop) (C : Prop) (D : Prop) (h₁ : C ↔ D) (h₂ : A ↔ B) (h₃ : A ↔ D) : B ↔ D => **Robo**: Ja aber du musst ihm helfen umzuschreiben. Mit `rw [h₁]` kannst du `C` durch `D`
"Man kann auch rückwärts umschreiben: ersetzen."
`rw [←h₂]` ersetzt man im Goal `B` durch `a` (`\\l`, also ein kleines L)" rw [h₁]
Hint "**Du** Und wenn ich in die andere Richtung umschreiben möchte?
HiddenHint (A : Prop) (B : Prop) (h : A ↔ B) : A ↔ B =>
"Schau mal durch die Annahmen durch."
-- These should not be necessary if they don't use `rw [] at`.
HiddenHint (A : Prop) (B : Prop) (C : Prop) (D : Prop) (h₁ : C ↔ D) (h₂ : A ↔ B) (h₃ : A ↔ C) : B ↔ C =>
"Auch eine Möglichkeit... Kannst du das Goal so umschreiben,
dass es mit einer Annahme übereinstimmt?"
HiddenHint (A : Prop) (B : Prop) (C : Prop) (D : Prop) (h₁ : C ↔ D) (h₂ : A ↔ B) (h₃ : B ↔ D) : B ↔ C =>
"Auch eine Möglichkeit.. Kannst du das Goal so umschreiben, dass es mit einer Annahme übereinstimmt?"
Hint (A : Prop) (B : Prop) (h : B ↔ A) : A ↔ B => **Robo**: Dann schreibst du ein `←` vor den Namen, also `rw [← hₓ]`."
"Naja auch Umwege führen ans Ziel... Wenn du das Goal zu `A ↔ A` umschreibst, kann man es mit Branch
`rfl` beweisen (rsp. das passiert automatisch.)" rw [← h₃]
Hint "**Du**: Ehm, das ist verkehrt.
Hint (A : Prop) (B : Prop) (C : Prop) (D : Prop) (h₁ : C ↔ D) (h₂ : D ↔ B) (h₃ : D ↔ A) : B ↔ C => **Robo**: Andersrum wär's besser gewesen, aber wenn du jetzt einfach weitermachst bis du
"Das ist nicht der optimale Weg..." sowas wie `A ↔ A` kriegst, kann `rfl` das beweisen.
Hint (A : Prop) (B : Prop) (C : Prop) (D : Prop) (h₁ : C ↔ D) (h₂ : D ↔ B) (h₃ : A ↔ D) : B ↔ C => **Robo: Da fällt mir ein, `rw` versucht automatisch `rfl` am Ende. Das heisst, du musst
"Das ist nicht der optimale Weg..." das nicht einmal mehr schreiben."
rw [h₂]
rw [←h₂]
assumption
Conclusion "Ihr geht weiter und der Operationsleiter zeigt euch die Küche."
NewTactics rw assumption NewTactic rw assumption
DisabledTactic tauto
-- NewLemma Iff.symm

@ -10,35 +10,35 @@ Title "Genau dann wenn"
Introduction Introduction
" "
Nun schauen wir uns Option 1) an, die du schon von UND kennst: Der Koch kommt erfreut hinter einem grossen Topf hervor.
1. Mit `h.mp` und `h.mpr` (oder `h.1` und `h.2`) kann man die einzelnen Implikationen **Koch**: Sagt mal, gestern hat mir jemand was erzählt und es will einfach nicht aus
direkt auswählen. meinem Kopf…
`h.mp` und `h.mpr` (oder `h.1` und `h.2`) sind die einzelnen Implikationen, und du kannst
mit denen ensprechend arbeiten. Insbesondere kannst du mit `apply h.mp` die Implikation
$A \\Rightarrow B$ anwenden, wenn das Goal $B$ ist.
*(PS: das `.mp` kommt von \"Modus Ponens\", ein Ausdruck as der Logik.)*
" "
Statement Statement (A B C : Prop) (h : A ↔ B) (g : B → C) : A → C := by
"Angenommen man hat $A \\iff B$ und $B \\Rightarrow C$, zeige $A \\Rightarrow C$." Hint "**Du**: Naja ich kann wohl immerhin mal mit `intro` anfangen und annehmen,
(A B C : Prop) (h : A ↔ B) (g : B → C) : A → C := by dass `{A}` wahr sei…
**Robo**: und dann schauen wir weiter!"
intro hA intro hA
Hint "**Robo**: Also eine Implikation wendet man mit apply an…
**Du**: Weiss ich ja!"
apply g apply g
apply h.mp Hint "**Robo**: …und du kannst die Implikation `{A} → {B}` genau gleich mit
assumption `apply {h}.mp` anwenden.
HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : A ↔ B) (g : B → C) : A → C => **Du**: Aber ich könnte hier auch `rw [← h]` sagen, oder?
"Fange wie immer mit `intro` an."
HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : A ↔ B) (g : B → C) (hA : A) : C => **Robo**: Klar, aber offenbar versteht der Koch das `rw` nicht.
"Wie im Implikationen-Level kannst du nun `apply` verwenden." "
apply h.mp
assumption
Hint (A : Prop) (B : Prop) (C : Prop) (h : A ↔ B) (g : B → C) (hA : A) : B => Conclusion "**Koch**: Danke vielmals! Jetzt muss ich aber schauen dass die Suppe nicht verkocht!
"Mit `apply h.mp` kannst du nun die Implikation `A → B` anwenden."
Conclusion "Im nächsten Level findest du die zweite Option." Und er eilt davon."
NewTactics apply assumption NewTactic apply assumption
DisabledTactic tauto rw

@ -10,33 +10,35 @@ Title "Genau dann wenn"
Introduction Introduction
" "
Und noch die letzte Option: Noch während der Koch wieder zu seiner Suppe geht, kommt sein erster Gehilfe hervor.
2. Mit `rcases h with ⟨h₁, h₂⟩` könnte man die Struktur `h` zerlegen und man erhält zwei **Gehilfe**: Ich hab gestern noch was anderes gehört, könnt ihr mir da auch helfen?
separate Annahmen `(h₁ : A → B)` und `(h₂ : B → A)` Aber ich versteh nicht ganz was ihr meinem Chef erklärt habt.
Als letzte Option kannst du `rcases h with ⟨h₁, h₂⟩` auch auf eine Annahme `(h : A ↔ B)`
anwenden, genau wie du dies bei einer Annahme `(h' : A ∧ B)` gemacht hast.
" "
Statement
"Angenommen man hat $A \\iff B$ und $B \\Rightarrow C$, zeige $A \\Rightarrow C$." Statement (A B : Prop) : (A ↔ B) → (A → B) := by
(A B : Prop) : (A ↔ B) → (A → B) := by Hint "**Du**: Hmm, mindestens mit der Implikation kann ich anfangen."
Hint (hidden := true) "**Robo**: Genau, das war `intro`."
intro h intro h
rcases h Hint "**Du**: Also ich kenn `rw [h]` und `apply h.mp`, aber das will er wohl nicht hören.
**Robo**: Was du machen könntest ist mit `rcases h with ⟨mp, mpr⟩` die Annahme in zwei
Teile aufteilen."
Branch
intro a
Hint "**Robo**: Hier müsstest du jetzt `rw [←h]` oder `apply h.mp` benützen, aber der
Gehilfe will, dass du zwingend eine dritte Variante benützt. Geh doch einen
Schritt zurück so dass das Goal `A → B` ist."
rcases h with ⟨mp, mpr⟩
Hint (hidden := true) "**Du**: Ah und jetzt ist das Resultat in den Annahmen."
assumption assumption
HiddenHint (A : Prop) (B : Prop) : (A ↔ B) → A → B =>
"Angefangen mit `intro h` kannst du annehmen, dass `(h : A ↔ B)` wahr ist."
HiddenHint (A : Prop) (B : Prop) (h : A ↔ B) : A → B =>
"Mit `rcases h with ⟨h₁, h₂⟩` kannst du jetzt die Annahme `(h : A ↔ B)` zerlegen."
Conclusion Conclusion
" "
**Gehilfe**: Ah danke! Und jetzt versteh ich auch die Zusammenhänge!
" "
OnlyTactic intro rcases assumption
NewTactics intro apply rcases assumption DisabledTactic rw apply tauto
-- -- TODO: The new `cases` works differntly. There is also `cases'` -- -- TODO: The new `cases` works differntly. There is also `cases'`
-- example (A B : Prop) : (A ↔ B) → (A → B) := by -- example (A B : Prop) : (A ↔ B) → (A → B) := by

@ -11,45 +11,39 @@ Title "Lemmas"
Introduction Introduction
" "
Bewiesene Resultate möchte man in späteren Aufgaben als Sätze wieder verwenden. Ihr setzt euch hin und der Gehilfe bringt euch allen Suppe. Neben euch sitzt eine Mechanikerin
über ihre Suppe geneigt.
In Lean sind das Lemmas und Theoreme (wobei es keinen Unterschied zwischen `lemma` und `theorem` **Mechanikerin**: Sagt mal, ich hab unten über folgendes tiefgründiges Problem nachgedacht:
gibt). "
Links in der Bibliothek findest du bekannte Lemmas. Wenn das Goal mit der Aussage des Lemmas
übereinstimmt, kannst du es mit `apply [Lemma-Name]` anwenden, wie du das mit Implikationen auch
machst.
Zum Beispiel gibt es ein Lemma, das sagt, dass wenn Statement (A : Prop) : ¬A A := by
$A \\Rightarrow B$ dann $(\\neg A \\lor B)$: Hint "**Du**: Das scheint ziemlich offensichtlich.
``` **Robo**: Ich glaube, sie will eine detailierte Antwort. Ich kenne ein Lemma
lemma not_or_of_imp : (A B : Prop) (h : A → B) : `not_or_of_imp`, was sagt `(A → B) → ¬ A B`. Da das Resultat des Lemmas mit
¬A B := by deinem Goal übreinstimmt, kannst du es mit `apply not_or_of_imp` anwenden."
... Branch
``` right
Hint "**Du**: Und jetzt?
Wenn also dein Goal `¬A B` ist, kannst du mit `apply not_or_of_imp` dieses **Robo**: `right/left` funktioniert hier nicht, da du nicht weisst ob `A` wahr oder falsch
Lemma anwenden. Danach musst du noch alle Annahmen zeigen. ist."
" Branch
left
Hint "**Du**: Und jetzt?
Statement **Robo**: `right/left` funktioniert hier nicht, da du nicht weisst ob `A` wahr oder falsch
"Benutze `not_or_of_imp` um zu zeigen, dass $\\neg A \\lor A$ wahr ist." ist."
(A : Prop) : ¬A A := by
apply not_or_of_imp apply not_or_of_imp
Hint (hidden := true) "**Robo**: Ich würd wieder mit `intro` weitermachen."
intro intro
assumption assumption
HiddenHint (A : Prop) : ¬A A =>
"Das Lemma wendest du mit `apply not_or_of_imp` an."
HiddenHint (A : Prop) : A → A =>
"Wie immer, `intro` ist dein Freund."
Conclusion Conclusion
" "
**Mechanikerin**: Danke vielmals, jetzt bin ich schon viel ruhiger.
" "
NewTactics apply left right assumption NewLemma not_or_of_imp
DisabledTactic tauto
NewLemmas not_or_of_imp

@ -0,0 +1,38 @@
import TestGame.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.Cases
import Mathlib
Game "TestGame"
World "Implication"
Level 11
Title "by_cases"
Introduction
"
**Du**: Sagt mal, hätte ich da nicht auch einfach zwei Fälle anschauen können:
Wenn `A` wahr ist, beweis ich die rechte Seite, sonst die Linke.
**Robo**: Tatsächlich, `by_cases h : A` würde genau das machen!
"
Statement (A : Prop) : ¬A A := by
Hint (hidden := true) "**Du**: Wie?
**Robo**: Also `by_cases h : A` erstellt zwei Goals. Im ersten hast Du `(h : A)` zur
Verfügung, im zweiten `(h : ¬ A)`."
by_cases h : A
Hint "**Du**: "
right
assumption
left
assumption
Conclusion
"
**Du**: Das kann noch ganz nützlich sein.
"
NewTactic by_cases
DisabledTactic tauto

@ -1,39 +0,0 @@
import TestGame.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.Cases
import Mathlib.Logic.Basic
Game "TestGame"
World "Implication"
Level 11
Title "Lemmas"
Introduction
"
Wenn die Aussage eines Lemmas eine Äquivalenz ist, kann man dieses auch mit `rw` brauchen,
wie du es von Äquivalenzen kennst.
Ein wichtiges Lemma ist dass $\\neg(\\neg A))$ und $A$ äquivalent sind:
```
lemma not_not (A : Prop) : ¬¬A ↔ A := by
...
```
Mit `rw [not_not]` sucht Lean also im Goal ein Term `¬¬(·)` und entfernt dort das `¬¬`.
"
Statement
"Zeige, dass $A (¬¬B) ∧ C$ und $A B ∧ C$ äquivalent sind."
(A B C : Prop) : A (¬¬B) ∧ C ↔ A B ∧ C := by
rw [not_not]
Conclusion
"
`rw` hat automatisch `rfl` ausgeführt und dadurch den Beweis beendet.
"
NewTactics rw
NewLemmas not_not not_or_of_imp

@ -0,0 +1,50 @@
import TestGame.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.Cases
import Mathlib.Logic.Basic
Game "TestGame"
World "Implication"
Level 12
Title "Lemmas"
Introduction
"
Der Arbeitskollegin der Mechanikerin, der die ganze Zeit gespannt zugehört hat, dreht sich zu
euch.
Er ist offensichtlich interessiert and existierenden Resultaten zu sein, aber offenbar
kann er nicht viel mit `apply` anfangen.
Er hat aber folgendes Resultat bereit:
```
lemma not_not (A : Prop) : ¬¬A ↔ A
```
und stellt euch folgende Frage:
"
Statement (A B C : Prop) : (A ∧ (¬¬C)) (¬¬B) ∧ C ↔ (A ∧ C) B ∧ (¬¬C) := by
Hint "**Robo**: Ein Lemma, das wie `not_not` ein `↔` oder `=` im Statement hat, kann
auch mit `rw [not_not]` verwendet werden."
rw [not_not]
Hint "**Du**: Häh, wieso hat das jetzt 2 von 3 der `¬¬` umgeschrieben?
**Robo**: `rw` schreibt nur das erste um, das es findet, also `¬¬C`. Aber weil dieses
mehrmals vorkommt, werden die alle ersetzt…
**Du**: Ah, und `¬¬B` ist was anderes, also brauch ich das Lemma nochmals."
rw [not_not]
Conclusion
"
**Du**: Ah und wir sind fertig…?
**Robo**: Ja, `rw` versucht immer anschliessend `rfl` aufzurufen, und das hat hier
funktioniert.
"
DisabledTactic tauto apply
NewLemma not_not

@ -1,81 +0,0 @@
import TestGame.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight
import Mathlib
set_option tactic.hygienic false
Game "TestGame"
World "Implication"
Level 12
Title "Zusammenfassung"
Introduction
"
Damit bist du am Ende der zweiten Lektion angekommen.
Hier ein Überblick über alles was in diesem Kapitel eingeführt wurde und eine
Abschlussaufgabe.
## Notationen / Begriffe
| | Beschreibung |
|:--------------|:---------------------------------------------------------|
| → | Eine Implikation. |
| ↔ | Genau-dann-wenn / Äquivalenz. |
| `lemma` | Ein Resultat mit einem Namen. |
| `theorem` | Das gleiche wie ein Lemma. |
| `example` | Wie ein Lemma aber ohne Namen (nicht weiter verwendbar.) |
## Taktiken
| | Taktik | Beispiel |
|:----|:--------------------------|:-------------------------------------------------------|
| 8 | `intro` | Für eine Implikation im Goal. |
| 9 | `revert` | Umkehrung von `intro`. |
| 10 | `apply` | Wendet eine Implikation auf das Goal an. |
| 10ᵇ | `apply` | Wendet ein Lemma an. |
| 11 | `rw` | Umschreiben zweier äquivalenter Aussagen. |
| 11ᵇ | `rw` | Benutzt ein Lemma, dessen Aussage eine Äquivalenz ist. |
Als Abschlussübung kannst du folgende Äquivalenz zeigen:
"
Statement imp_iff_not_or
"$A \\Rightarrow B$ ist äquivalent zu $\\neg A \\lor B$."
(A B : Prop) : (A → B) ↔ ¬ A B := by
constructor
apply not_or_of_imp
intro h ha
rcases h with h | h
contradiction
assumption
HiddenHint (A : Prop) (B: Prop) : (A → B) ↔ ¬ A B =>
"Eine Äquivalenz im Goal geht man immer mit `constructor` an."
HiddenHint (A : Prop) (B: Prop) : (A → B) → ¬ A B =>
"Diese Aussage hast du vorhin bereits als Lemma kennengelernt und angewendet."
HiddenHint (A : Prop) (B: Prop) (h : A → B) : ¬ A B =>
"Diese Aussage hast du vorhin bereits als Lemma kennengelernt und angewendet."
HiddenHint (A : Prop) (B: Prop) : ¬ A B → (A → B) =>
"Eine Implikation geht man fast immer mit `intro h` an."
HiddenHint (A : Prop) (B: Prop) (h : ¬ A B) : (A → B) =>
"Nochmals `intro`."
HiddenHint (A : Prop) (B: Prop) (h : ¬ A B) : (A → B) =>
"Das ODER in den Annahmen kannst du mit `rcases h with h | h` aufteilen."
HiddenHint (A : Prop) (B: Prop) (h : ¬ A B) (ha : A) : B =>
"Das ODER in den Annahmen kannst du mit `rcases h with h | h` aufteilen."
HiddenHint (A : Prop) (B: Prop) (ha : A) (ha' : ¬A) : B =>
"Findest du einen Widerspruch?"
NewTactics rfl assumption trivial left right constructor rcases
NewLemmas not_not not_or_of_imp

@ -0,0 +1,77 @@
import TestGame.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight
import Mathlib
set_option tactic.hygienic false
Game "TestGame"
World "Implication"
Level 13
Title "Zusammenfassung"
Introduction
"
**Operationsleiter**: Damit endet unsere Führung langsam. Bevor ihr weitergeht
habe ich noch ein Problem, an dem ich mir die Zähne ausbeisse. Wir haben die
Herleitung eines unserer Programme `imp_iff_not_or` verloren, und wissen nicht mehr
ob es einwandfrei funktioniert.
**Du**: Nah gut, mal sehen. Robo, was hab ich denn alles hier gelernt?
**Robo**: Hier ist die Übersicht:
## Notationen / Begriffe
| | Beschreibung |
|:--------------|:---------------------------------------------------------|
| → | Eine Implikation. |
| ↔ | Genau-dann-wenn / Äquivalenz. |
## Taktiken
| | Taktik | Beispiel |
|:----|:--------------------------|:-------------------------------------------------------|
| 8 | `intro` | Für eine Implikation im Goal. |
| 9 | `revert` | Umkehrung von `intro`. |
| 10 | `apply` | Wendet eine Implikation auf das Goal an. |
| 10ᵇ | `apply` | Wendet ein Lemma an. |
| 11 | `by_cases` | Fallunterscheidung `P` und `¬P` |
| 12 | `rw` | Umschreiben zweier äquivalenter Aussagen. |
| 12ᵇ | `rw` | Benutzt ein Lemma, dessen Aussage eine Äquivalenz ist. |
"
Statement imp_iff_not_or (A B : Prop) : (A → B) ↔ ¬ A B := by
constructor
Hint "**Du**: Das sieht kompliziert aus…
**Robo** *(flüsternd)*: Ja, aber die Richtung kennst du ja schon also Lemma,
wend doch einfach das an."
apply not_or_of_imp
Hint "**Du**: Gibt es für die Gegenrichtung auch ein Lemma?
**Robo**: Leider nicht. Da musst du manuell ran."
Hint (hidden := true) "**Robo**: Na Implikationen fangst du immer mit `intro` an."
intro h
intro ha
Hint (hidden := true) "**Robo**: Ich wür mal die Annahme `h` mit `rcases` aufteilen."
rcases h with h | h
contradiction
assumption
DisabledTactic tauto
Conclusion "**Operationsleiter**: Damit gehen unsere Wege auseinander. Da fällt mir ein, seit
ihr auf dem Weg zu unserem Schwestermond?
**Du**: Könnten wir sein…
**Operationsleiter**: Ich hab hier einen Brief für *Evenine*, könntet ihr diesen mit euch führen?
**Du**: Klar! Robo, halt den mal.
Robo nimmt den Brief und lässt ihn irgendwo in seinem Innern verschwinden. Dabei bemerkt er
den besorgten Blick des Operationsleiters.
**Robo**: Keine Angst, ich verdaue nichts!"

@ -24,7 +24,7 @@ Statement
rw [Nat.pow_succ, Nat.mul_succ, add_assoc, h, mul_comm, ←mul_add] rw [Nat.pow_succ, Nat.mul_succ, add_assoc, h, mul_comm, ←mul_add]
simp simp
--NewLemmas Nat.pow_succ, Nat.mul_succ, add_assoc, mul_comm, ←mul_add --NewLemma Nat.pow_succ, Nat.mul_succ, add_assoc, mul_comm, ←mul_add
-- example (n : ) : Even (n^2 + n) := by -- example (n : ) : Even (n^2 + n) := by
-- induction n -- induction n

@ -34,8 +34,8 @@ Statement Nat.pos_iff_ne_zero
intro intro
apply Nat.succ_pos apply Nat.succ_pos
NewTactics simp NewTactic simp
NewLemmas Nat.succ_pos NewLemma Nat.succ_pos
Hint : 0 < Nat.zero ↔ Nat.zero ≠ 0 => Hint : 0 < Nat.zero ↔ Nat.zero ≠ 0 =>
"Den Induktionsanfang kannst du oft mit `simp` lösen." "Den Induktionsanfang kannst du oft mit `simp` lösen."

@ -18,6 +18,6 @@ Statement
(n : ) (h : 2 ≤ n) : n ≠ 0 := by (n : ) (h : 2 ≤ n) : n ≠ 0 := by
linarith linarith
NewTactics linarith NewTactic linarith
NewLemmas Nat.pos_iff_ne_zero NewLemma Nat.pos_iff_ne_zero

@ -38,4 +38,4 @@ Hint (P : → Prop) (m : ) (hi : P m) : P (Nat.succ m) =>
In diesem Beispiel kannst du `apply` benützen." In diesem Beispiel kannst du `apply` benützen."
NewTactics induction NewTactic induction

@ -43,9 +43,9 @@ Statement
rw [Fin.sum_univ_castSucc (n := m + 1)] rw [Fin.sum_univ_castSucc (n := m + 1)]
rfl rfl
OnlyTactics rw rfl OnlyTactic rw rfl
NewLemmas Fin.sum_univ_castSucc NewLemma Fin.sum_univ_castSucc
HiddenHint (m : ) : HiddenHint (m : ) :
∑ i : Fin (m + 1), (i : ) + (m + 1) = ∑ i : Fin (Nat.succ m + 1), ↑i => ∑ i : Fin (m + 1), (i : ) + (m + 1) = ∑ i : Fin (Nat.succ m + 1), ↑i =>

@ -49,9 +49,9 @@ Statement
rw [Fin.sum_univ_castSucc (n := m + 1)] rw [Fin.sum_univ_castSucc (n := m + 1)]
rfl rfl
OnlyTactics rw rfl OnlyTactic rw rfl
NewLemmas Fin.sum_univ_castSucc NewLemma Fin.sum_univ_castSucc
Hint (m : ) : Hint (m : ) :
∑ i : Fin (m + 1), (i : ) + (m + 1) = ∑ i : Fin (Nat.succ m + 1), ↑i => ∑ i : Fin (m + 1), (i : ) + (m + 1) = ∑ i : Fin (Nat.succ m + 1), ↑i =>

@ -67,4 +67,4 @@ Hint (A : Prop) (B : Prop) (C : Prop) (h : A ∧ B) : C ∧ A =>
Hint (A : Prop) (B : Prop) (C : Prop) (h : A → C) : C ∧ A => Hint (A : Prop) (B : Prop) (C : Prop) (h : A → C) : C ∧ A =>
"Ein UND im Goal kann mit `constructor` aufgeteilt werden." "Ein UND im Goal kann mit `constructor` aufgeteilt werden."
NewTactics left right assumption constructor rcases NewTactic left right assumption constructor rcases

@ -20,4 +20,4 @@ Conclusion
" "
" "
NewTactics ring NewTactic ring

@ -20,4 +20,4 @@ Conclusion
" "
" "
NewTactics ring NewTactic ring

@ -58,4 +58,4 @@ Hint (n : ) (hn : odd n) (h : ∀ (x : ), (odd x) → even (x + 1)) : even
"`∀ (x : ), (odd x) → even (x + 1)` ist eigentlich das gleiche wie "`∀ (x : ), (odd x) → even (x + 1)` ist eigentlich das gleiche wie
`(x : ) → `" `(x : ) → `"
NewTactics ring intro unfold NewTactic ring intro unfold

@ -22,4 +22,4 @@ Statement : ∃ (f : ), ∀ (x : ), f x = 0 := by
Conclusion "" Conclusion ""
NewTactics use simp NewTactic use simp

@ -62,28 +62,30 @@ Im nachfolgenden beweisen wir die Eigenschaften einzeln.
Statement Statement
"Zeige, dass $\\mathbb{R}$ ein $\\mathbb{Q}$-Modul ist." "Zeige, dass $\\mathbb{R}$ ein $\\mathbb{Q}$-Modul ist."
: Module := : Module := by
{ smul := fun a r ↦ ↑a * r, refine {
smul_zero := by smul := fun a r => ↑a * r
intro a smul_zero := ?smul_zero
rw [smul_zero] zero_smul := ?zero_smul
zero_smul := by one_smul := ?one_smul
intro a smul_add := ?smul_add
change (0 : ) * a = 0 add_smul := ?add_smul
simp mul_smul := ?mul_smul }
one_smul := by
intro b · intro b
change (1 : ) * b = b change (1 : ) * b = b
rw [Rat.cast_one, one_mul] rw [Rat.cast_one, one_mul]
smul_add := by · intro x y b
intro a x y change (x * y : ) * b = x * (y * b)
change a * (x + y) = a * x + a * y rw [Rat.cast_mul, mul_assoc]
rw [mul_add] · intro a
add_smul := by rw [smul_zero]
intro r s x · intro a x y
change (r + s : ) * x = r * x + s * x change a * (x + y) = a * x + a * y
rw [Rat.cast_add, add_mul] rw [mul_add]
mul_smul := by · intro r s x
intro x y b change (r + s : ) * x = r * x + s * x
change (x * y : ) * b = x * (y * b) rw [Rat.cast_add, add_mul]
rw [Rat.cast_mul, mul_assoc]} · intro a
change (0 : ) * a = 0
simp

@ -72,4 +72,4 @@ Statement
simp <;> simp <;>
ring ring
NewTactics fin_cases funext NewTactic fin_cases funext

@ -40,4 +40,4 @@ Statement
-- fin_cases i; -- fin_cases i;
-- simp, -- simp,
--NewLemmas top_le_span_range_iff_forall_exists_fun le_top --NewLemma top_le_span_range_iff_forall_exists_fun le_top

@ -4,10 +4,24 @@ import TestGame.Levels.Predicate.L03_Rewrite
import TestGame.Levels.Predicate.L04_Ring import TestGame.Levels.Predicate.L04_Ring
import TestGame.Levels.Predicate.L05_Rfl import TestGame.Levels.Predicate.L05_Rfl
import TestGame.Levels.Predicate.L06_Exists import TestGame.Levels.Predicate.L06_Exists
import TestGame.Levels.Predicate.L07_Forall import TestGame.Levels.Predicate.L07_Exists
import TestGame.Levels.Predicate.L08_PushNeg import TestGame.Levels.Predicate.L08_Forall
import TestGame.Levels.Predicate.L09_Summary import TestGame.Levels.Predicate.L09_PushNeg
Game "TestGame" Game "TestGame"
World "Predicate" World "Predicate"
Title "Prädikate" Title "Prädikate"
Introduction "Eure Reise geht weiter zum zweiten Mond. Dieser ist etwas grösser und
komplett mit buschartigen Pflanzen überwachsen. Der Landeanflug sit etwas kniffliger,
aber offenbar kann Robo nicht nur Lean übersetzen, sondern auch mit maschineller Genauigkeit
navigieren.
Ihr werdet sofort empfangen und man führt euch zur lokalen Machthaberin.
Ihr kommt an verschiedensten riesigen Büschen vorbei, die offensichtlich innen Ausgehöhlt sind
und so als Wände für Behausungen der Wesen hier dienen.
Man führt euch in einen der Büsche. Nun entdecksts du dass das Blätterdach künstlich verstärkt ist,
offensichtlich um Regen abzuweisen.
Vor euch steht eine Frau, die euch als Machthabering *Evenine* vorgestellt wird."

@ -11,34 +11,26 @@ Title "Natürliche Zahlen"
Introduction Introduction
" "
Wir sind den narürlichen Zahlen `` (`\\N`) schon kurz begegnet. **Evenine**: Willkommen Reisende! Wir leben hier in Einklang mit der Natur und allem natürlichen,
so sagt mir, könnt ihr mit natürlichen Zahlen umgehen?
"
Gleichungen, die nur die Operationen `+, -, *, ^` (Addition, Subtraktion, Multiplikation, Potenz) Statement (x y : ) : (x + y) ^ 2 = x ^ 2 + 2 * x * y + y ^ 2 := by
und Variablen enthalten, kann Lean mit der Taktik `ring` beweisen. Hint "**Du**: Das hab ich in der Schule gelernt, man rechnet das einfach aus,
indem man die Terme umsortiert.
Diese Taktik funktioniert nicht nur über den natürlichen Zahlen, **Robo**: Behaupte doch mit `ring`, dass das so ist.
sondern auch in (kommutativen) Gruppen, Ringen, und Körpern. Sie heisst `ring`, weil sie für Ringe
entwickelt wurde.
(Note: Division `/` ignorieren wir hier erst einmal, weil diese auf `` **Du**: Aber `` ist doch gar kein Ring?
nicht kanonisch definiert ist.)
"
Statement **Robo**: `ring` funktioniert schon für Halbringe, aber sie heisst ring, weil sie auf
"Zeige $(x + y) ^ 2 = x ^ 2 + 2 * x * y + y ^ 2$." (kommutativen) Ringen am besten funktioniert.
(x y : ) : (x + y) ^ 2 = x ^ 2 + 2 * x * y + y ^ 2 := by "
ring ring
HiddenHint (x : ) (y : ) : (x + y) ^ 2 = x ^ 2 + 2 * x * y + y ^ 2 =>
"`ring` übernimmt den ganzen Spaß."
Conclusion Conclusion
" "
Die Taktik heisst übrigens `ring` weil sie dafür entwickelt wurde, Gleichungen in einem abstrakten *Evenine: Ja das stimmt schon. Und das genügt uns auf diesem Planet auch als Antwort.*
Ring zu lösen, funktioniert aber auch auf ``, auch wenn dieses kein Ring ist
(erst `` ist ein Ring).
" "
NewTactics ring NewTactic ring
#eval 4 / 6

@ -9,62 +9,31 @@ Title "Rewrite"
Introduction Introduction
" "
Wir haben gesehen, dass man mit `rw` Äquivalenzen wie `A ↔ B` brauchen kann, um im Goal Robo spuckt den Brief aus, den er dabei hatte, und gibt ihn *Evenine*.
$A$ durch $B$ zu ersetzen.
Genau gleich kann man auch Gleichungen `a = b` mit `rw` verwenden. **Evenine**: Das verstehe ich nicht, wisst ihr was damit gemeint ist?
Und sie händigt Dir den Brief:
" "
Statement umschreiben Statement (a b c d : ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : b = c := by
"Angenommen man hat die Gleichheiten Hint "**Du**: Schau mal, das ist ja fast genau, was wir auf *Implis* gemacht haben,
nur jetzt mit Gleichheiten von Zahlen anstatt Genau-Dann-Wenn-Aussagen!
$$ **Robo**: `=` und `↔` kannst du praktisch gleich behandeln wenns um `rw` geht."
\\begin{aligned} a &= b \\\\ a &= d \\\\ c &= d \\end{aligned} Hint (hidden := true) "**Du**: Also auch `rw [hₓ]` und `rw [← hₓ]`?
$$
Zeige dass $b = c$." **Robo**: Probiers doch einfach."
(a b c d : ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : b = c := by
rw [h₁] rw [h₁]
Hint (hidden := true) "**Du**: Wie war das nochmals mit rückwärts umschreiben?
**Robo**: `←` ist `\\l`. Und dann `rw [← hₓ]`"
rw [←h₂] rw [←h₂]
assumption assumption
HiddenHint (a : ) (b : ) (c : ) (d : ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : b = c => Conclusion
"Im Goal kommt `c` vor und `h₁` sagt `c = d`. "
Probiers doch mit `rw [h₁]`." **Evenine**: Danke viemals, das hilft uns vermutlich, jetzt Frage ich mich aber…
"
HiddenHint (a : ) (b : ) (c : ) (d : ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : a = c =>
"Im Goal kommt `C` vor und `h₁` sagt `C ↔ D`.
Probiers doch mit `rw [h₁]`."
HiddenHint (a : ) (b : ) (c : ) (d : ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : b = d =>
" Man kann auch rückwärts umschreiben: `h₂` sagt `a = b` mit
`rw [←h₂]` ersetzt man im Goal `b` durch `a` (`\\l`, also ein kleines L)"
HiddenHint (a : ) (b : ) (h : a = b) : a = b =>
"Schau mal durch die Annahmen durch."
-- These should not be necessary if they don't use `rw [] at`.
HiddenHint (a : ) (b : ) (c : ) (d : ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = c) : b = c =>
"Auch eine Möglichkeit... Kannst du das Goal so umschreiben,
dass es mit einer Annahme übereinstimmt?"
HiddenHint (a : ) (b : ) (c : ) (d : ) (h₁ : c = d) (h₂ : a = b) (h₃ : b = d) : b = c =>
"Auch eine Möglichkeit.. Kannst du das Goal so umschreiben, dass es mit einer Annahme übereinstimmt?"
Hint (a : ) (b : ) (h : b = a) : a = b =>
"Naja auch Umwege führen ans Ziel... Wenn du das Goal zu `a = a` umschreibst, kann man es mit
`rfl` beweisen (rsp. das passiert automatisch.)"
Hint (a : ) (b : ) (c : ) (d : ) (h₁ : c = d) (h₂ : d = b) (h₃ : d = a) : b = c =>
"das ist nicht der optimale Weg..."
Hint (a : ) (b : ) (c : ) (d : ) (h₁ : c = d) (h₂ : d = b) (h₃ : a = d) : b = c =>
"das ist nicht der optimale Weg..."
Conclusion "Übrigens, mit `rw [h₁, ←h₂]` könnte man mehrere `rw` zusammenfassen."
NewTactics assumption rw NewTactic assumption rw

@ -9,32 +9,28 @@ Title "Rewrite"
Introduction Introduction
" "
Als Übung erinnern wir daran, dass man mit `rw [h] at g` auch in anderen Annahmen umschreiben **Evenine**: Mit diesem neuen Wissen, könnt ihr mir bei folgendem helfen:
kann:
Wenn `(h : X = Y)` ist, dann ersetzt `rw [h] at g` in der Annahme
`g` das `X` durch `Y`.
" "
Statement umschreiben Statement
"Angenommen man hat die Gleichheiten "
$$ $$
\\begin{aligned} a &= b \\\\ a + a ^ 2 &= b + 1 \\end{aligned} \\begin{aligned}
a &= b \\\\
a + a ^ 2 &= b + 1 \\\\
\\vdash b + b ^ 2 = b + 1
\\end{aligned}
$$ $$
"
Zeige dass $b + b ^ 2 = b + 1$."
(a b : ) (h : a = b) (g : a + a ^ 2 = b + 1) : b + b ^ 2 = b + 1 := by (a b : ) (h : a = b) (g : a + a ^ 2 = b + 1) : b + b ^ 2 = b + 1 := by
Hint "**Du**: Ah da ersetzt man ja einfach `{a}` durch `{b}` in der anderen Annahme!
**Robo**: Genau! Das machst du mit `rw [{h}] at {g}`."
rw [h] at g rw [h] at g
Hint (hidden := true) "**Robo**: Schau mal durch die Annahmen."
assumption assumption
Hint (a : ) (b : ) (h : a = b) (g : a + a ^ 2 = b + 1) : b + b ^ 2 = b + 1 => Conclusion "
"`rw [ ... ] at g` schreibt die Annahme `g` um." **Robo**: Noch ein Trick: Mit `rw [h] at *` kann man im weiteren `h` in **allen** Annahmen und
dem Goal umschreiben.
Hint (a : ) (b : ) (h : a = b) (g : a + a ^ 2 = b + 1) : a + a ^ 2 = a + 1 => "
"Sackgasse. probiers doch mit `rw [h] at g` stattdessen."
Conclusion "Übrigens, mit `rw [h] at *` kann man im weiteren `h` in **allen** Annahmen und
dem Goal umschreiben."
NewTactics assumption rw

@ -9,25 +9,27 @@ Title "Natürliche Zahlen"
Introduction Introduction
" "
Oft braucht man eine Kombination von `rw` und `ring` um Gleichungen zu lösen. *Evenines* Berater meldet sich.
Zuerst setzt man mit `rw` die vorhandenen Annahmen ein, und sobald die beiden Seiten **Berater**: Das stimmt wohl, aber das Problem, das uns eigentlich beschäftigt hat, eure
einer Gleichung im Goal rechnerisch gleich sind, kan `ring` dies beweisen. Natürlichkeit, war folgendes:
" "
Statement Statement
"Angenommen, man hat die Gleichung $x = 2 * y + 1$, zeige (x y z : ) (h : x = 2 * y + 1) (g : z = 3 * y + 1): x ^ 2 = 4 * y ^ 2 + z + y := by
$x ^ 2 = 4 * y ^ 2 + 3 * y + 1 + y$. " Hint "**Du**: Ich versteh das Pattern. Wenn ich zuerst alles so umschreibe, dass
(x y : ) (h : x = 2 * y + 1) : x ^ 2 = 4 * y ^ 2 + 3 * y + 1 + y := by das Goal nur noch rechnen und umsortieren ist, dann kann `ring` den Rest machen!
rw [h]
ring
Hint (x : ) (y : ) (h : x = 2 * y + 1) : x ^ 2 = 4 * y ^ 2 + 3 * y + 1 + y => **Robo**: Noch ein Trick: Entweder kannst du zwei Befehle `rw [h₁]` und `rw [h₂]` schreiben,
"Die Annahme `h` kannst du mit `rw [h]` benützen." oder du kannst das gleich in einem machen : rw [h₁, h₂]."
rw [h, g]
ring
Hint (y : ) : (2 * y + 1) ^ 2 = 4 * y ^ 2 + 3 * y + 1 + y =>
"Jetzt kann `ring` übernehmen."
Conclusion "" Conclusion
"
*Evenine* bedankt sich nochmals für die Botschaft und ihr werdet zu einem kleineren Busch geführt,
der ein gemütlich warmes Innenleben an den Tag legt.
"
NewTactics ring rw NewTactic ring rw

@ -8,24 +8,29 @@ Title "Definitionally equal"
Introduction Introduction
" "
Als kleine Nebenbemerkung: Müde ruht ihr euch in eurer Bleibe aus. Du schaust doch eine Lücke in den Blättern vielen
kleinen Vögeln bei der Nahrungssuche zu.
Auf Grund der Implementation in Lean brauchst du die Taktik `ring` gar nicht, wenn **Du**: Sag mal Robo, ich hab vorhin ein Kind überhört, dass seinem Spielgefährten erklärt hat,
du Gleichungen über `` hast, die keine Variablen enthalten: folgendes sei mit `rfl` zu beweisen:
So ist zum Beispiel `2` als `1 + 1` definiert, deshalb kannst du $1 + 1 = 2$ einfach mit
`rfl` beweisen.
" "
Statement Statement : 1 + 1 = 2 := by
"Zeige dass $1 + 1$ zwei ist." : Hint "**Du**: Wieso nicht `ring`?
1 + 1 = 2 := by
**Robo**: Klar, `ring` geht auch und ist intuitiver.
Für `rfl` kommt es darauf an, wie die Sachen genau definiert sind: `1 + 1` ist als
`(0.succ).succ` definiert und `2` halt ebenfalls.
"
rfl rfl
OnlyTactic rfl
Conclusion Conclusion
" "
**Notiz:** Die meisten anderen Taktiken versuchen am Schluss automatisch `rfl` **Du**: Dann war das mehr Glück?
aufzurufen, deshalb brauchst du das nur noch selten.
" **Robo**: Das ist eine Art die Welt zu sehen…
NewTactics rfl Damit fällst du in einen ruhigen Schlaf.
"

@ -6,6 +6,8 @@ import Mathlib.Tactic.Ring
import Mathlib.Algebra.Parity import Mathlib.Algebra.Parity
set_option tactic.hygienic false
Game "TestGame" Game "TestGame"
World "Predicate" World "Predicate"
Level 6 Level 6
@ -14,58 +16,58 @@ Title "Gerade/Ungerade"
Introduction Introduction
" "
Gerade/ungerade werden in Lean wie folgt definiert: Am nächsten Tag erklärt euch *Evenine*, dass es auf dem Mond zwei Gruppierungen gibt,
ihre und die ihres Halbbruders *Oddeus*. Die Mottos sind
``` ```
def Even (n : ) : Prop := ∃ r, n = r + r def Even (n : ) : Prop := ∃ r, n = r + r
def Odd (n : ) : Prop := ∃ r, n = r + r + 1
``` ```
Also dadurch, dass ein `(r : )` existiert sodass `n = r + r (+1)`.
Beachte das Komma `,` welches die Variablen des `∃` (`\\exists`) von der Aussage trennen. und
Hierzu gibt es 3 wichtige Taktiken: ```
def Odd (n : ) : Prop := ∃ r, n = 2 * r + 1
1) Definitionen wie `Even` kann man mit `unfold Even at *` im Infoview einsetzen. ```
Das ändert Lean-intern nichts und ist nur für den Benutzer. Man kann auch einen
Term `(h : Even x)` einfach so behandeln als wäre es ein Term `(h : ∃ r, x = 2 * r)`. **Evenine**: Hier, ich zeige euch mal etwas was man bei uns machen kann:
2) Bei einer Annahme `(h : ∃ r, ...)` kann man mit `rcases h with ⟨y, hy⟩` ein solches `y`
Auswählen, dass die Annahme `h` erfüllt.
3) Bei einem `∃` im Goal muss man ein Element `y` angeben, welches diese Aussage erfüllen
soll. Das macht man mit `use y`
" "
Statement even_square Statement even_square (n : ) (h : Even n) : Even (n ^ 2) := by
"Wenn $n$ gerade ist, dann ist $n^2$ gerade." Branch
(n : ) (h : Even n) : Even (n ^ 2) := by unfold Even
Hint "Rob**: Am besten machst du auch noch `unfold Even at h`, damit du verstehst was los ist."
Hint "**Robo**: Wie du oben siehst, ist `Even n` dadurch definiert,
dass ein `r` existiert so dass `r + r = n`. Am besten
öffnest du diese Definition mit `unfold Even at *` einmal, dann siehst du besser, was los ist. "
unfold Even at * unfold Even at *
rcases h with ⟨x, hx⟩ Hint "**Du**: Also von `{h}` weiss ich jetzt dass ein `r` existiert, so dass `r + r = n`…
use 2 * x ^ 2
rw [hx]
ring
Hint (n : ) (h : Even n) : Even (n ^ 2) => **Robo**: Mit `rcases h with ⟨r, hr⟩` kannst du dieses `r` tatsächlich einführen."
"Wenn du die Definition von `Even` nicht kennst, kannst du diese mit `unfold Even` oder rcases h with ⟨r, hr⟩
`unfold Even at *` ersetzen. Hint "**Du**: Und jetzt muss ich eine passende Zahl finden, so dass `x + x = n^2`?
Note: Der Befehl macht erst mal nichts in Lean sondern nur in der Anzeige. Der Beweis funktioniert
genau gleich, wenn du das `unfold` rauslöscht."
Hint (n : ) (h : ∃ r, n = r + r) : ∃ r, n ^ 2 = r + r => **Robo**: Genau. Und mit `use _` gibst du diese Zahl an."
"Ein `∃ x, ..` in den Annahmen kann man wieder mit `rcases h with ⟨x, hx⟩` aufteilen, und Hint (hidden := true) "**Robo**: Also sowas ähnliches wie `use 4 * r ^ 3`, aber ich kann
ein `x` erhalten, dass die Aussage erfüllt." dir leider nicht sagen, welche Zahl passt.
"
Branch
rw [hr]
Hint "**Robo**: Das geht auch, jetzt musst du aber wirklich `use` verwenden."
use 2 * r ^ 2
ring
use 2 * r ^ 2
Hint "**Du**: Ah und jetzt `ring`!
Hint (n : ) (x : ) (hx : n = x + x) : ∃ r, n ^ 2 = r + r => **Robo**: Aber zuerst must du noch mit
"Bei einem `∃ x, ..` im Goal hingegen, muss man mit `use y` das Element angeben, dass `rw` `n` durch `r + r` ersetzen, da `ring` das sonst nicht weiss."
die Aussage erfüllen soll." rw [hr]
ring
Hint (n : ) (x : ) (hx : n = x + x) : ∃ r, (x + x) ^ 2 = r + r =>
"Bei einem `∃ x, ..` im Goal hingegen, muss man mit `use y` das Element angeben, dass
die Aussage erfüllen soll."
Hint (n : ) (x : ) (hx : n = x + x) : n ^ 2 = 2 * x ^ 2 + 2 * x ^ 2 => -- TODO: [Comment] For me the expected behaviour of `(strict := true)` would
"Prinzipiell löst `ring` simple Gleichungen wie diese. Allerdings musst du zuerst `n` zu -- be that it distinguishes between the defEq states while `(strict := false)`
`x + x` umschreiben..." -- would show the hint regardless of a `unfold Even`.
Hint (n : ) (x : ) (hx : n = x + x) : (x + x) ^ 2 = 2 * x ^ 2 + 2 * x ^ 2 => NewTactic unfold use
"Die Taktik `ring` löst solche Gleichungen." NewDefinition Even Odd
NewTactics unfold rcases use rw ring Conclusion "**Evenine**: Seht ihr?"
NewDefinitions Even Odd

@ -0,0 +1,43 @@
import TestGame.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.Contrapose
import Mathlib.Tactic.Use
import Mathlib.Tactic.Ring
import Mathlib.Algebra.Parity
set_option tactic.hygienic false
Game "TestGame"
World "Predicate"
Level 7
Title "Gerade/Ungerade"
Introduction
"
**Du**: Aber, das sagt doch gar nichts aus, genau das gleiche könnte ich für `Odd`
auch sagen. Hier seht!
"
Statement odd_square (n : ) (h : Odd n) : Odd (n ^ 2) := by
unfold Odd at *
rcases h with ⟨r, hr⟩
Hint "**Robo**: Ich hab noch einen Trick auf Lager:
Wenn du jetzt herausfinden willst, welche Zahl du einsetzen musst, könntest
du schon jetzt mit `rw [{hr}]` weitermachen…"
rw [hr]
Hint "**Robo**: Wenn du jetzt `ring` benötigst, dann schreibt es einfach alles in
Normalform um, das hilft beim vergleichen."
ring
Hint "**Du**: Was bedeutet `ring_nf`?
**Robo**: Wenn man `ring` nicht am Schluss benützt, sollte man stattdessen `ring_nf`
schreiben, aber die funktionieren praktisch gleich."
use 2 * (r + r ^ 2)
ring
-- TODO: Allow `ring_nf` as part of `ring`.
Conclusion "**Evenine**: Tatsächlich. Vielleicht sind wir gar nich tso unterschiedlich. Könntet
ihr mal mit ihm reden gehen?"

@ -1,53 +0,0 @@
import TestGame.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.Contrapose
import Mathlib.Tactic.Use
import Mathlib.Tactic.Ring
import Mathlib.Algebra.Parity
import Mathlib
Game "TestGame"
World "Predicate"
Level 7
Title "Für alle"
Introduction
"
Zum `∃` gehört auch das \"für alle\" `∀` (`\\forall`).
Der Syntax ist `∀ (n : ), 0 ≤ n` also wie beim `∃` trennt ein Komma die Annahmen von der Aussage.
Eine `∀`-Aussage in den Annahmen verhält sich so wie ein Lemma. Das heisst man kann
auch diese mit `apply` und `rw` anwenden, je nachdem was die Aussage nach dem Komma ist.
Also folgende Annahme und Lemma sind genau :
- `(le_square : ∀ (n : ), n ≤ n^2)`
- `lemma le_square (n : ) : n ≤ n^2`
Ein `∀` im Goal kann man mit `intro` angehen, genau wie bei einer Implikation `→`.
TODO: 1-2 Aufgaben mehr.
"
Statement
" Für alle natürlichen Zahlen $x$ gilt, falls $x$ gerade ist, dann ist $x + 1$
ungerade." : ∀ (x : ), (Even x) → Odd (1 + x) := by
intro x h
unfold Even at h
unfold Odd
rcases h with ⟨y, hy⟩
use y
rw [hy]
ring
Hint (n : ) (hn : Odd n) (h : ∀ (x : ), (Odd x) → Even (x + 1)) : Even (n + 1) =>
"`∀ (x : ), (odd x) → even (x + 1)` ist eigentlich das gleiche wie
`(x : ) → `"
Conclusion "Für-alle-Statements, Implikationen und Lemmas/Theoreme verhalten sich alle
praktisch gleich. Mehr dazu später."
NewTactics ring intro unfold

@ -0,0 +1,53 @@
import TestGame.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.Contrapose
import Mathlib.Tactic.Use
import Mathlib.Tactic.Ring
import Mathlib.Algebra.Parity
import Mathlib
Game "TestGame"
World "Predicate"
Level 8
Title "Für alle"
Introduction
"
Ihr macht euch also auf den Weg. Unterwegs trefft ihr einen Händler und ihr lässt euch von
ihm den Weg zeigen, sowie einige Tipps zu den beiden Geschwistern geben.
**Händler**: Also seht, die beiden sind gar nicht so verschieden. Ein altes Sprichwort sagt:
"
-- Zum `∃` gehört auch das \"für alle\" `∀` (`\\forall`).
-- Der Syntax ist `∀ (n : ), 0 ≤ n` also wie beim `∃` trennt ein Komma die Annahmen von der Aussage.
-- Eine `∀`-Aussage in den Annahmen verhält sich so wie ein Lemma. Das heisst man kann
-- auch diese mit `apply` und `rw` anwenden, je nachdem was die Aussage nach dem Komma ist.
-- Also folgende Annahme und Lemma sind genau :
-- - `(le_square : ∀ (n : ), n ≤ n^2)`
-- - `lemma le_square (n : ) : n ≤ n^2`
-- Ein `∀` im Goal kann man mit `intro` angehen, genau wie bei einer Implikation `→`.
-- TODO: 1-2 Aufgaben mehr.
Statement : ∀ (x : ), (Even x) → Odd (1 + x) := by
Hint "**Du**: Das `∀` heisst sicher \"für alle\".
**Robo**: Und man schreibt `\\forall`. Ein `∀ x, …` im Goal kannst du wie eine
Implikation mit `intro x` angehen."
intro x h
unfold Even at h
unfold Odd
rcases h with ⟨y, hy⟩
use y
rw [hy]
ring
Conclusion "**Händler**: Sichere Reise!"

@ -1,67 +0,0 @@
import TestGame.Metadata
import Mathlib.Tactic.PushNeg
import Mathlib
import Mathlib.Algebra.Parity
import TestGame.ToBePorted
Game "TestGame"
World "Predicate"
Level 8
Title "PushNeg"
Introduction
"
Zum Schluss, immer wenn man irgendwo eine Verneinung `¬∃` oder `¬∀` sieht (`\\not`), kann man
mit `push_neg` das `¬` durch den Quantor hindurchschieben.
Das braucht intern die Lemmas
- `not_exists (A : Prop) : ¬ (∃ x, A) ↔ ∀x, (¬A)`
- `not_forall (A : Prop) : ¬ (∀ x, A) ↔ ∃x, (¬A)`
(welche man auch mit `rw` explizit benutzen könnte.)
"
Statement
"Es existiert keine natürliche Zahl $n$, sodass $n + k$ immer ungerade ist.":
¬ ∃ (n : ), ∀ (k : ) , Odd (n + k) := by
push_neg
intro n
use n
rw [not_odd]
unfold Even
use n
Hint : ¬ ∃ (n : ), ∀ (k : ) , Odd (n + k) =>
"`push_neg` schiebt die Negierung an den Quantoren vorbei."
Hint (n : ) : (∃ k, ¬Odd (n + k)) =>
"An dieser Stelle musst du nun ein `k` angeben, sodass `n + k` gerade ist... Benutze `use`
mit der richtigen Zahl."
HiddenHint (n : ) : ¬Odd (n + n) =>
"Du kennst ein Lemma um mit `¬odd` umzugehen."
-- HiddenHint (n : ) (k : ) : ¬odd (n + k) =>
-- "Du kennst ein Lemma um mit `¬odd` umzugehen."
HiddenHint (n : ) : Even (n + n) =>
"`unfold even` hilft, anzuschauen, was hinter `even` steckt.
Danach musst du wieder mit `use r` ein `(r : )` angeben, dass du benützen möchtest."
-- HiddenHint (n : ) (k : ) : even (n + k) =>
-- "`unfold even` hilft hier weiter."
Hint (n : ) : n + n = 2 * n => "Recap: `ring` löst Gleichungen in ``."
Conclusion ""
NewTactics push_neg intro use rw unfold ring
NewDefinitions Even Odd
NewLemmas not_even not_odd not_exists not_forall

@ -0,0 +1,109 @@
import TestGame.Metadata
import Mathlib.Tactic.PushNeg
import Mathlib
import Mathlib.Algebra.Parity
import TestGame.ToBePorted
Game "TestGame"
World "Predicate"
Level 9
Title "PushNeg"
Introduction
"
Das Dorf von *Oddeus* scheint stärker befestigt zu sein, all jenes von *Evenine*. Ihr kommt
and eine Wand aus Dornenranken. Beim Eingang steht eine Wache, die auch zuruft:
"
-- Zum Schluss, immer wenn man irgendwo eine Verneinung `¬∃` oder `¬∀` sieht (`\\not`), kann man
-- mit `push_neg` das `¬` durch den Quantor hindurchschieben.
-- Das braucht intern die Lemmas
-- - `not_exists (A : Prop) : ¬ (∃ x, A) ↔ ∀x, (¬A)`
-- - `not_forall (A : Prop) : ¬ (∀ x, A) ↔ ∃x, (¬A)`
-- (welche man auch mit `rw` explizit benutzen könnte.)
open Nat
Statement : ¬ ∃ (n : ), ∀ (k : ) , Odd (n + k) := by
Hint "**Du**: Also ich kann mal das `¬` durch die Quantifier hindurchschieben.
**Robo**: `push_neg` macht genau das!
**Robo**: Intern braucht das zwei Lemmas
```
not_exists (A : Prop) : ¬ (∃ x, A) ↔ ∀x, (¬A)
not_forall (A : Prop) : ¬ (∀ x, A) ↔ ∃x, (¬A)
```
die du natürlich auch mit `rw` gebrauchen könntest."
Branch
unfold Odd
push_neg
Hint "**Robo**: Der Weg ist etwas schwieriger. Ich würde nochmals zurück und schauen,
dass du irgendwann `¬Odd` kriegst, was du dann mit `rw [←even_iff_not_odd]`
zu `Even` umwandeln.
kannst."
push_neg
intro n
Hint "**Robo**: Welche Zahl du jetzt mit `use` brauchst, danach wirst du vermutlich das
Lemma `←even_iff_not_odd` brauchen.
**Du**: Könnte ich jetzt schon `rw [←even_iff_not_odd]` machen?
**Robo**: Ne, `rw` kann nicht innerhalb von Quantifiern umschreiben.
**Du**: Aber wie würde ich das machen?
**Robo**: Zeig ich dir später, die Wache wird schon ganz ungeduldig!
Im Moment würde ich zuerst mit `use` eine richtige Zahl angeben, und danach umschreiben."
Branch
use n + 2
Hint "**Robo**: Gute Wahl! Jetzt kannst du `←even_iff_not_odd` verwenden."
Branch
use n + 4
Hint "**Robo**: Gute Wahl! Jetzt kannst du `←even_iff_not_odd` verwenden."
use n
Hint "**Robo**: Gute Wahl! Jetzt kannst du `←even_iff_not_odd` verwenden."
rw [←even_iff_not_odd]
unfold Even
use n
--ring
Conclusion "Damit werdet ihr eingelassen.
**Robo**: Entweder wir suchen direkt diesen *Oddeus*, oder wir schauen uns einmal um. Die Wahl
ist deine!
**Du**: Kannst du mir nochmals einen Überblick geben, was wir gelernt haben?
**Robo**:
| | Beschreibung |
|:--------------|:----------------------------|
| `` | Die natürlichen Zahlen. |
| `∃` | Existential-Quantifier |
| `∀` | Forall-Quantifier |
| `Even n` | `n` ist gerade |
| `Odd n` | `n` ist ungerade |
| | Taktik | Beispiel |
|:------|:--------------------------|:-------------------------------------------------------|
| *12ᶜ* | `rw` | Umschreiben mit Gleichungen. |
| 13 | `ring` | Löst Gleichungen mit `+, -, *, ^`. |
| 14 | `unfold` | Setzt visuell die Bedeutung einer Definition ein. |
| 15 | `use` | Um ein `∃` im Goal anzugehen. |
| *7ᶜ* | `rcases h with ⟨x, hx⟩` | Um ein `∃` in den Annahmen zu zerlegen. |
| *8ᵇ* | `intro` | Um ein `∀` im Goal anzugehen. |
| 16 | `push_neg` | Für `¬∃` und `¬∀` im Goal. |
"
NewTactic push_neg
NewLemma even_iff_not_odd odd_iff_not_even not_exists not_forall

@ -1,55 +0,0 @@
import TestGame.Metadata
import Mathlib.Tactic.PushNeg
import Mathlib
import TestGame.ToBePorted
Game "TestGame"
World "Predicate"
Level 9
Title "Zusammenfassung"
Introduction
"
Damit bist du am Ende der dritten Lektion angekommen.
Hier ein Überblick über alles was in diesem Kapitel eingeführt wurde und eine
Abschlussaufgabe.
## Notationen / Begriffe
| | Beschreibung |
|:--------------|:----------------------------|
| `` | Die natürlichen Zahlen. |
| `∃` | Existential-Quantifier |
| `∀` | Forall-Quantifier |
| `Even n` | `n` ist gerade |
| `Odd n` | `n` ist ungerade |
## Taktiken
| | Taktik | Beispiel |
|:------|:--------------------------|:-------------------------------------------------------|
| *11ᶜ* | `rw` | Umschreiben mit Gleichungen. |
| 12 | `ring` | Löst Gleichungen mit `+, -, *, ^`. |
| 13 | `unfold` | Setzt visuell die Bedeutung einer Definition ein. |
| 14 | `use` | Um ein `∃` im Goal anzugehen. |
| *7ᶜ* | `rcases h with ⟨x, hx⟩` | Um ein `∃` in den Annahmen zu zerlegen. |
| *8ᵇ* | `intro` | Um ein `∀` im Goal anzugehen. |
| 15 | `push_neg` | Für `¬∃` und `¬∀` im Goal. |
Als Abschlussübung kannst du folgende Äquivalenz zeigen:
"
Statement
"TODO":
True := by
trivial
Conclusion ""
NewTactics push_neg intro use rw unfold ring
NewDefinitions Even Odd
NewLemmas not_even not_odd not_exists not_forall

@ -43,4 +43,4 @@ Statement
rcases h with ⟨_, h₂⟩ rcases h with ⟨_, h₂⟩
assumption assumption
NewLemmas Nat.prime_def_lt'' NewLemma Nat.prime_def_lt''

@ -21,24 +21,20 @@ Du siehst Robo hilflos an.
Statement "" Statement ""
(A B C : Prop) : (A B C : Prop) :
¬((¬B ¬ C) (A → B)) → (¬A B) ∧ ¬ (B ∧ C) := by ¬((¬B ¬ C) (A → B)) → (¬A B) ∧ ¬ (B ∧ C) := by
tauto Hint "**Robo** Das ist ganz einfach. Mit `{A} {B} {C} : Prop` meint sie:
`{A}`, `{B}` und `{C}` sind irgendwelche Aussagen (*propositions*).
Hint (A B C : Prop) : Und mit `→` meint sie ⇒, also “impliziert”. Die anderen Symbole kennst Du, oder?
¬((¬B ¬ C) (A → B)) → (¬A B) ∧ ¬ (B ∧ C) =>
"**Robo** Das ist ganz einfach. Mit `{A} {B} {C} : Prop` meint sie:
`{A}`, `{B}` und `{C}` sind irgendwelche Aussagen (*propositions*).
Und mit `→` meint sie ⇒, also “impliziert”. Die anderen Symbole kennst Du, oder?
**Du** Ehhm, ja. Aber da muss ich jetzt trotzdem erst einmal überlegen. **Du** Ehhm, ja. Aber da muss ich jetzt trotzdem erst einmal überlegen.
**Robo** (flüsternd) Behaupte doch einfach, dass sei eine Tautologie. **Robo** (flüsternd) Behaupte doch einfach, dass sei eine Tautologie.
**Du** Ernsthaft? **Du** Ernsthaft?
**Robo** Ja. Schreib einfach `tauto`. **Robo** Ja. Schreib einfach `tauto`.
**Robo** Mach schon … **Robo** Mach schon …"
" tauto
Conclusion Conclusion
" "
@ -47,4 +43,4 @@ Aber glaubt bloß nicht, dass Ihr damit auf *diesem* Planeten viel weiterkommt!
Meine Untertanen verstehen `tauto` nicht. Da müsst Ihr Euch schon etwas mehr anstrengen. Meine Untertanen verstehen `tauto` nicht. Da müsst Ihr Euch schon etwas mehr anstrengen.
" "
NewTactics tauto NewTactic tauto

@ -19,17 +19,14 @@ Er schreibt es Dir wieder auf.
Statement "" : Statement "" :
42 = 42 := by 42 = 42 := by
Hint " **Robo** Ist doch klar. Du musst ihn einfach daran erinnern,
dass Gleichheit *reflexiv* ist. Probier mal `rfl`."
rfl rfl
Hint : 42 = 42 => "
**Robo** Ist doch klar. Du musst ihn einfach daran erinnern, dass Gleichheit *reflexiv* ist.
Probier mal `rfl`.
"
Conclusion Conclusion
" "
**Untertan** Ah, richtig. Ja, Sie haben ja so recht. Das vergesse ich immer. Rfl, rfl, rfl … **Untertan** Ah, richtig. Ja, Sie haben ja so recht. Das vergesse ich immer. Rfl, rfl, rfl …
" "
NewTactics rfl NewTactic rfl
DisabledTactics tauto DisabledTactic tauto

@ -13,9 +13,7 @@ Während der erste Untertan noch rfl, rfl, rfl murmelt, tritt schon der nächste
Statement "" Statement ""
(n : ) (h₁ : 10 > n) (h₂ : 1 < n) (h₃ : n ≠ 5) : 1 < n := by (n : ) (h₁ : 10 > n) (h₂ : 1 < n) (h₃ : n ≠ 5) : 1 < n := by
assumption Hint "
Hint (n : ) (h₁ : 10 > n) (h₂ : 1 < n) (h₃ : n ≠ 5) : 1 < n => "
**Robo** `{n} : ` bedeutet, `{n}` ist eine natürliche Zahl. **Robo** `{n} : ` bedeutet, `{n}` ist eine natürliche Zahl.
**Du** Warum schreibt er dann nicht `{n} ∈ `?? **Du** Warum schreibt er dann nicht `{n} ∈ `??
@ -32,8 +30,8 @@ für die Annahme `n < 10`, `1 < n` und `n ≠ 5`. Beweisen sollen wir: `1 < n`.
**Du** ??? **Du** ???
**Robo** Du musst ihm das halt explizit sagen. Probiers mal mit `assumption`. **Robo** Du musst ihm das halt explizit sagen. Probiers mal mit `assumption`."
" assumption
Conclusion Conclusion
" "
@ -41,5 +39,5 @@ Conclusion
zerbrochen habe! zerbrochen habe!
" "
NewTactics assumption NewTactic assumption
DisabledTactics tauto DisabledTactic tauto

@ -14,9 +14,7 @@ Ein dritter Untertan kommt mit folgendem Problem.
Statement "" Statement ""
(A : Prop) (hA : A) : A := by (A : Prop) (hA : A) : A := by
assumption Hint "
Hint (A : Prop) (hA : A) : A => "
**Robo** Hier bedeutet `{A} : Prop` wieder, dass `{A}` irgendeine Aussage ist. **Robo** Hier bedeutet `{A} : Prop` wieder, dass `{A}` irgendeine Aussage ist.
Und `{hA}` ist eine Name für die Annahme, dass `{A}` wahr ist. Und `{hA}` ist eine Name für die Annahme, dass `{A}` wahr ist.
@ -24,15 +22,15 @@ Hint (A : Prop) (hA : A) : A => "
**Robo** Ja. Da kommst Du jetzt selbst drauf, wie das geht, oder? **Robo** Ja. Da kommst Du jetzt selbst drauf, wie das geht, oder?
" "
Hint (hidden := true) "Ist doch genau wie eben:
HiddenHint (A : Prop) (hA : A) : A => die Aussage, die zu beweisen ist, gehört selbst zu den Annahmen.
"Ist doch genau wie eben: die Aussage, die zu beweisen ist, gehört selbst zu den Annahmen.
Also wird `asumption` auch wieder funktionieren." Also wird `asumption` auch wieder funktionieren."
assumption
Conclusion Conclusion
" "
**Untertan** Das ging ja schnell. Super! Vielen Dank. **Untertan** Das ging ja schnell. Super! Vielen Dank.
" "
NewTactics assumption NewTactic assumption
DisabledTactics tauto DisabledTactic tauto

@ -11,11 +11,8 @@ Introduction
Der nächste Untertan in der Reihe ist ein Schelm. Der nächste Untertan in der Reihe ist ein Schelm.
" "
Statement "" : Statement "" : True := by
True := by Hint "
trivial
Hint : True => "
**Robo** Dieses `True` ist eine spezielle Aussage, nämlich die Aussage, die immer und **Robo** Dieses `True` ist eine spezielle Aussage, nämlich die Aussage, die immer und
bedingungslos wahr ist. bedingungslos wahr ist.
@ -23,6 +20,7 @@ bedingungslos wahr ist.
**Robo** Ich glaube, nichts. Ich glaube, Du kannst einfach `trivial` schreiben. **Robo** Ich glaube, nichts. Ich glaube, Du kannst einfach `trivial` schreiben.
" "
trivial
Conclusion Conclusion
" "
@ -35,5 +33,5 @@ Wie in einer Mathe-Vorlesung?
Das funktioniert nur in einer Handvoll Situationen. Das funktioniert nur in einer Handvoll Situationen.
" "
NewTactics trivial NewTactic trivial
DisabledTactics tauto DisabledTactic tauto

@ -11,27 +11,24 @@ Introduction
Der Schelm hat noch eine Schwester dabei. Der Schelm hat noch eine Schwester dabei.
" "
Statement "" : Statement "" : ¬False := by
¬False := by Hint "
trivial **Robo** Dieses Zeichen `¬` bedeutet Negation. Also wenn eine Aussage `(A : Prop)`
wahr ist, dann ist `¬A` falsch, und umgekehrt.
Hint : ¬False => "
**Robo** Dieses Zeichen `¬` bedeutet Negation. Also wenn eine Aussage `(A : Prop)`
wahr ist, dann ist `¬A` falsch, und umgekehrt.
**Du** Und `False` ist wahrscheinlich die Aussage, die immer falsch ist? **Du** Und `False` ist wahrscheinlich die Aussage, die immer falsch ist?
**Robo** Ja, richtig. **Robo** Ja, richtig.
**Du** Ist das jetzt nicht doch wieder trivial? **Du** Ist das jetzt nicht doch wieder trivial?
**Robo** Probier mal! **Robo** Probier mal!"
" trivial
Conclusion Conclusion
" "
Die Schwester lacht und eilt ihrem Bruder hinterher. Die Schwester lacht und eilt ihrem Bruder hinterher.
" "
NewTactics trivial NewTactic trivial
DisabledTactics tauto DisabledTactic tauto

@ -15,12 +15,8 @@ Als nächstes kommen drei Querulanten. Der erste hat folgendes Problem:
Statement "" Statement ""
(A : Prop) (h : False) : A := by (A : Prop) (h : False) : A := by
contradiction Hint "**Du** Wenn ich das jetzt richtig lese, ist `{A}` eine Aussage,
und wir haben außerdem eine Annahme names `{h}`, die besagt …
Hint (A : Prop) (h : False) : A =>
"
**Du** Wenn ich das jetzt richtig lese, ist `{A}` eine Aussage, und wir haben außerdem eine
Annahme names `{h}`, die besagt …
**Robo** … die besagt, dass `False` gilt. **Robo** … die besagt, dass `False` gilt.
@ -33,8 +29,8 @@ Insbesondere die gesuchte Aussage `{A}`.
**Du** Und wie erkläre ich das jetzt diesem Formalosophen? **Du** Und wie erkläre ich das jetzt diesem Formalosophen?
**Robo** Ich glaube, Du musst ihn darauf hinweisen, dass zwischen der allgemeingültigen **Robo** Ich glaube, Du musst ihn darauf hinweisen, dass zwischen der allgemeingültigen
Annahme `True` und seiner Annahme `False` ein Widerspruch besteht. Probier mal `contradiction`. Annahme `True` und seiner Annahme `False` ein Widerspruch besteht. Probier mal `contradiction`."
" contradiction
Conclusion Conclusion
"Der erste Querulant ist offenbar zufrieden. "Der erste Querulant ist offenbar zufrieden.
@ -45,5 +41,5 @@ Conclusion
wir haben eine `contradiction` in unserem Annahmen, also folgt jede beliebige Aussage. wir haben eine `contradiction` in unserem Annahmen, also folgt jede beliebige Aussage.
" "
NewTactics contradiction NewTactic contradiction
DisabledTactics tauto DisabledTactic tauto

@ -17,14 +17,10 @@ Auftritt zweiter Querulant.
Statement "" Statement ""
(n : ) (h : n ≠ n) : n = 37 := by (n : ) (h : n ≠ n) : n = 37 := by
contradiction Hint "**Du** Ist `{n} ≠ {n}` nicht auch ein Widerspruch?
Hint (n : ) (h : n ≠ n) : n = 37 =>
"
**Du** Ist `{n} ≠ {n}` nicht auch ein Widerspruch?
**Robo** Probiers mal! **Robo** Probiers mal!"
" contradiction
Conclusion Conclusion
" "
@ -39,5 +35,5 @@ Und gleich 38, und gleich 39, …
**Du** Ok, ok, verstehe. **Du** Ok, ok, verstehe.
" "
NewTactics contradiction NewTactic contradiction
DisabledTactics tauto DisabledTactic tauto

@ -17,14 +17,12 @@ Auftritt dritter Querulant.
Statement "" Statement ""
(n : ) (h : n = 10) (g : n ≠ 10) : n = 42 := by (n : ) (h : n = 10) (g : n ≠ 10) : n = 42 := by
Hint "**Du** Wieder ein Widerspruch in den Annahmen?
**Robo** Ich sehe, du hast langsam den Dreh raus."
contradiction contradiction
Hint (n : ) (h : n = 10) (g : n ≠ 10) : n = 42 =>
"
**Du** Wieder ein Widerspruch in den Annahmen?
**Robo** Ich sehe, du hast langsam den Dreh raus.
"
Conclusion Conclusion
" "
@ -33,5 +31,5 @@ worin der Widerspruch besteht: Die Annahme `n ≠ 10` ist genau die Negation vo
Man muss `≠` immer als `¬(· = ·)` lesen. Man muss `≠` immer als `¬(· = ·)` lesen.
" "
NewTactics contradiction NewTactic contradiction
DisabledTactics tauto DisabledTactic tauto

@ -15,34 +15,46 @@ Der nächste Formalosoph in der Reihe hat seine Frage bereìts mitgebracht.
Er legt sie uns vor, setzt sich hin, und häkelt. Er legt sie uns vor, setzt sich hin, und häkelt.
" "
Statement "" (A B : Prop) (hA : A) (hB : B) : A ∧ B := by Statement "" (A B : Prop) (hA : A) (hB : B) : A ∧ B := by
constructor Hint
assumption
assumption
Hint (A B : Prop) (hA : A) (hB : B) : A ∧ B =>
" "
**Du**: Also, wir haben zwei Annahmen: `{A}` gilt, und `{B}` gilt. Auch. Und beweisen sollen wir **Du**: Also, wir haben zwei Annahmen: `{A}` gilt, und `{B}` gilt. Auch. Und beweisen sollen wir
dass `{A} und {B}` gilt. Ich glaube, diese Formalospinner treiben mich noch zur Verzweiflung. dass `{A} und {B}` gilt. Ich glaube, diese Formalospinner treiben mich noch zur Verzweiflung.
Kann ich nicht wieder `trivial` sagen? Kann ich nicht wieder `trivial` sagen?
**Robo** Nee, diesmal wird das nicht funktionieren. **Robo**: Nee, diesmal wird das nicht funktionieren.
Du musst das Beweisziel einfach in zwei Teile zerlegen. Probier mal `constructor`. Du musst das Beweisziel einfach in zwei Teile zerlegen. Probier mal `constructor`.
**Du** Du meinst, `destructor`?? **Du**: Du meinst, `destructor`??
**Robo** Nein, `constructor`. Ich weiß das ist verwirrend, **Robo**: Nein, `constructor`. Ich weiß das ist verwirrend,
aber die nennen das hier so weil man die Aussage aus mehreren Teilen aber die nennen das hier so weil man die Aussage aus mehreren Teilen
konstruieren kann. konstruieren kann.
" "
constructor
HiddenHint (A : Prop) (hA : A) : A => -- TODO: (BUG) Hier werden beide der folgenden Hints angezeigt.
-- Das logiste Verhalten wäre, wenn jeder nur am richtigen Ort
-- angezeigt würde.
-- Ein cooles Feature wäre, wenn man nur diesen ersten Hint schreiben könnte
-- und dieser dann automatisch auch beim zweiten Goal angezeigt wird, aber dann mit `B` statt `A`.
Hint (hidden := true)
" "
**Robo** Schau mal, das ist Zauberpapier. **Robo**: Schau mal, das ist Zauberpapier.
Jetzt haben wir auf einmal zwei Beweisziele. Jetzt haben wir auf einmal zwei Beweisziele.
Hier ist dast Ziel `{A}`. Hier ist dast Ziel `{A}`.
Ich glaube, Du weißt schon, wie man die jeweils erreicht. Ich glaube, Du weißt schon, wie man die jeweils erreicht.
Die Ziele stehen ja jeweils in den *Annahmen*. Die Ziele stehen ja jeweils in den *Annahmen*.
" "
assumption
Hint (hidden := true)
"
**Robo**: Schau mal, das ist Zauberpapier.
Jetzt haben wir auf einmal zwei Beweisziele.
Hier ist dast Ziel `{B}`.
Ich glaube, Du weißt schon, wie man die jeweils erreicht.
Die Ziele stehen ja jeweils in den *Annahmen*.
"
assumption
Conclusion Conclusion
" "
@ -54,5 +66,5 @@ Ihm scheinen diese Fragen inzwischen Spaß zu machen.
Oder ist der nur gemalt? Probier mal! Oder ist der nur gemalt? Probier mal!
" "
NewTactics constructor NewTactic constructor
DisabledTactics tauto DisabledTactic tauto

@ -16,10 +16,7 @@ Langsam wird die Schlange kürzer. Die nächste Formalosophin, ebenfalls häkeln
Statement "" Statement ""
(A B C : Prop) (h : A ∧ (B ∧ C)) : B := by (A B C : Prop) (h : A ∧ (B ∧ C)) : B := by
rcases h with ⟨_, ⟨g , _⟩⟩ Hint "
assumption
Hint (A B C : Prop) (h : A ∧ (B ∧ C)) : B => "
**Du** Jetzt müssen wir wohl die Annahme de-konstruieren. **Du** Jetzt müssen wir wohl die Annahme de-konstruieren.
**Robo** Ja, genau. Das geht am einfachsten mit `rcases {h} with ⟨h₁, h₂⟩`. **Robo** Ja, genau. Das geht am einfachsten mit `rcases {h} with ⟨h₁, h₂⟩`.
@ -30,16 +27,12 @@ Hint (A B C : Prop) (h : A ∧ (B ∧ C)) : B => "
Und h₁ schreibst Du einfach als `h\\1`. Aber Du kannst Dir auch einfach andere Namen Und h₁ schreibst Du einfach als `h\\1`. Aber Du kannst Dir auch einfach andere Namen
für `h₁` und `h₂`, zum Beispiel `rcases {h} with ⟨hA, hBC⟩` für `h₁` und `h₂`, zum Beispiel `rcases {h} with ⟨hA, hBC⟩`
" "
Branch
Hint (A B C : Prop) (hA : A) (hAB : B ∧ C) : B => rcases h with ⟨h₁, h₂⟩
" Hint "**Robo** Das sieht doch schon besser aus! Gleich nochmal!"
**Robo** Das sieht doch schon besser aus! Gleich nochmal! rcases h with ⟨_, ⟨g , _⟩⟩
" Hint (hidden := true) "**Robo** Du hast einen Beweis dafür in den *Annahmen*."
assumption
HiddenHint (A : Prop) (hA : A) : A =>
"
**Robo** Du hast einen Beweis dafür in den *Annahmen*.
"
Conclusion Conclusion
" "
@ -47,5 +40,5 @@ Conclusion
`rcases h with ⟨h₁, ⟨h₂ , h₃⟩⟩`. `rcases h with ⟨h₁, ⟨h₂ , h₃⟩⟩`.
" "
NewTactics rcases NewTactic rcases
DisabledTactics tauto DisabledTactic tauto

@ -18,12 +18,7 @@ Der nächste bitte …
Statement Statement
"" ""
(A B : Prop) (hA : A) : A (¬ B) := by (A B : Prop) (hA : A) : A (¬ B) := by
left Hint "**Du** Muss ich jetzt wieder das Beweisziel de-konstruieren?
assumption
Hint (A B : Prop) (hA : A) : A (¬ B) =>
"
**Du** Muss ich jetzt wieder das Beweisziel de-konstruieren?
**Robo** Nein, viel einfacher. Wenn Du eine Oder-Aussage beweisen sollst, musst Du Dich **Robo** Nein, viel einfacher. Wenn Du eine Oder-Aussage beweisen sollst, musst Du Dich
einfach entscheiden, ob Du die linke oder rechte Seite beweisen willst. einfach entscheiden, ob Du die linke oder rechte Seite beweisen willst.
@ -31,18 +26,17 @@ einfach entscheiden, ob Du die linke oder rechte Seite beweisen willst.
**Du** Und wie erkläre ich meinem Formalosophen, welche Seite ich gern beweisen würde? **Du** Und wie erkläre ich meinem Formalosophen, welche Seite ich gern beweisen würde?
Ich will natürlich `{A}` beweisen! Ich will natürlich `{A}` beweisen!
**Robo** Mit `left` bzw. `right`. Ist doch logisch, oder? **Robo** Mit `left` bzw. `right`. Ist doch logisch, oder?"
" Branch
right
Hint (A B : Prop) (hA : A) : ¬ B => Hint "**Robo** Wusste gar nicht, dass Du eine Links-Rechts-Schwäche hast. Probier's nochmal."
" left
**Robo** Wusste gar nicht, dass Du eine Links-Rechts-Schwäche hast. Probier's nochmal. assumption
"
Conclusion Conclusion
" "
Auch dieser Formalosoph zieht zufrieden von dannen. Auch dieser Formalosoph zieht zufrieden von dannen.
" "
NewTactics left right assumption NewTactic left right assumption
DisabledTactics tauto DisabledTactic tauto

@ -20,14 +20,7 @@ Der nächste bitte …
Statement "" Statement ""
(A B : Prop) (h : (A ∧ B) A) : A := by (A B : Prop) (h : (A ∧ B) A) : A := by
rcases h with h | h Hint "**Robo** Schau mal, wenn du mit dem Finger eine Annahme berührst, zeigt es dir,
rcases h with ⟨h₁, h₂⟩
assumption
assumption
Hint (A B : Prop) (h :(A ∧ B) A) : A =>
"
**Robo** Schau mal, wenn du mit dem Finger eine Annahme berührst, zeigt es dir,
wie die Klammern gesetzt sind. Irre… wie die Klammern gesetzt sind. Irre…
**Du** Ah ich sehe, also `({A} ∧ {B}) {A}`! **Du** Ah ich sehe, also `({A} ∧ {B}) {A}`!
@ -41,34 +34,24 @@ Wir sind ja gleich hier fertig, und können zu einem interessanteren Planeten we
**Du** Also, wieder `rcases …`? **Du** Also, wieder `rcases …`?
**Robo** Ja, aber diesmal nicht `rcases {h} with ⟨h₁, h₂⟩`, sondern `rcases {h} with h | h`. **Robo** Ja, aber diesmal nicht `rcases {h} with ⟨h₁, h₂⟩`, sondern `rcases {h} with h | h`."
" rcases h with h | h
Hint "**Robo**
-- -- TODO: This also triggers later under the assumptions
-- -- `(A : Prop) (B : Prop) (h₁ : A) (h₂ : B) : A`
-- -- Could we do something about that?
-- Hint (A : Prop) (B : Prop) (h : A) : A =>
-- "
-- **Robo** Jetzt musst Du Dein Ziel zweimal beweisen:
-- Einmal unter Annahme der linken Seite `{A}`,
-- und einmal unter Annahme der rechten Seite `{A} {B}`. Hier haben nehmen wir an, die linke Seite
-- sei wahr.
-- "
Hint (A : Prop) (B : Prop) (h : A ∧ B) : A =>
"
**Robo**
Jetzt musst Du Dein Ziel zweimal beweisen: Jetzt musst Du Dein Ziel zweimal beweisen:
Einmal unter Annahme der linken Seite `{A} {B}`, Einmal unter Annahme der linken Seite `{A} {B}`,
und einmal unter Annahme der rechten Seite `{A}`. und einmal unter Annahme der rechten Seite `{A}`.
Hier haben nehmen wir an, die linke Seite Hier haben nehmen wir an, die linke Seite
sei wahr. sei wahr."
" Hint (hidden := true) " **Robo** Wie man mit einem Und in den Annahmen umgeht,
HiddenHint (A : Prop) (B : Prop) (h : A ∧ B) : A => weißt Du doch schon:
" `rcases h with ⟨h₁, h₂⟩`. Zur Erinnerung: Für die Klammern schreibst Du `\\<>`."
**Robo** Wie man mit einem Und in den Annahmen umgeht, weißt Du doch schon: rcases h with ⟨h₁, h₂⟩
`rcases h with ⟨h₁, h₂⟩`. Zur Erinnerung: Für die Klammern schreibst Du `\\<>`. Hint "**Robo** Jetzt musst Du Dein Ziel zweimal beweisen:
" Einmal unter Annahme der linken Seite `{A}`,
und einmal unter Annahme der rechten Seite `{A} {B}`. Hier haben nehmen wir an, die linke Seite
sei wahr."
assumption
assumption
Conclusion Conclusion
"**Du** Ok, das scheint ihn zufriedenzustellen. Nur noch eine Seele… "**Du** Ok, das scheint ihn zufriedenzustellen. Nur noch eine Seele…
@ -125,5 +108,5 @@ Die Worte, die Du aktiv gebrauchen musst, heißen zusammengefasst `Taktiken`. H
" "
NewTactics assumption rcases NewTactic assumption rcases
DisabledTactics tauto DisabledTactic tauto

@ -14,7 +14,9 @@ Introduction
" "
Der letzte Untertan tritt vor. Ihr Anliegen ist etwas komplizierter als die vorherigen. Der letzte Untertan tritt vor. Ihr Anliegen ist etwas komplizierter als die vorherigen.
**Robo** Wirf einfach alles drauf, was Du gelernt hast. Hier, ich bin sogar so nett und zeig Dir noch einmal die vier wichtigsten Taktiken für diese Situation an. **Robo** Wirf einfach alles drauf, was Du gelernt hast.
Hier, ich bin sogar so nett und zeig Dir noch einmal die vier
wichtigsten Taktiken für diese Situation an.
| (Übersicht) | Und (`∧`) | Oder (``) | | (Übersicht) | Und (`∧`) | Oder (``) |
|-------------|:-------------------------|:------------------------| |-------------|:-------------------------|:------------------------|
@ -26,46 +28,83 @@ Der letzte Untertan tritt vor. Ihr Anliegen ist etwas komplizierter als die vor
Statement "" Statement ""
(A B C : Prop) (h : A (B ∧ C)) : (A B) ∧ (A C) := by (A B C : Prop) (h : A (B ∧ C)) : (A B) ∧ (A C) := by
constructor Hint (hidden := true)
rcases h with h | h "**Robo**: Ich würd zuerst die Annahme {h} mit `rcases {h}` aufteilen."
left Branch
assumption constructor
rcases h with ⟨h₁, h₂⟩ · rcases h with h' | h'
right · left
assumption assumption
rcases h with h | h · rcases h' with ⟨h₁, h₂⟩
left right
assumption assumption
rcases h with ⟨h₁, h₂⟩ · rcases h with h' | h'
right · left
assumption assumption
· rcases h' with ⟨h₁, h₂⟩
HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : B ∧ C) : (A B) ∧ (A C) => right
"**Robo** Das `∧` in der Annahme kannst Du mit `rcases {h} with ⟨h₁, h₂⟩` zerlegen." assumption
rcases h
HiddenHint (A : Prop) (B : Prop) (C : Prop) : (A B) ∧ (A C) => Hint (hidden := true) "**Robo**: Jetzt kannst Du das `∧` im Goal mit `constructor` angehen."
"**Robo** Das `∧` im Goal kannst Du mit `constructor` zerlegen." · constructor
· left
HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : A (B ∧ C)) : (A B) ∧ (A C) => assumption
"**Robo** Das `` in der Annahme kannst Du mit `rcases h with h | h` zerlegen." · left
assumption
HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : A (B ∧ C)) : (A B) => · Hint (hidden := true)
"**Robo** Das `` in der Annahme kannst Du mit `rcases h with h | h` zerlegen." "**Robo**: Hier würde ich die Annahme {h} nochmals mit `rcases` aufteilen."
Branch
HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : A (B ∧ C)) : (A C) => constructor
"**Robo** Das `` in der Annahme kannst Du mit `rcases h with h | h` zerlegen." · Hint "**Robo**: Der Nachteil an der Reihenfolge ist, dass du jetzt in jedem Untergoal
`rcases h` aufrufen musst."
Branch
HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : B ∧ C) : (A B) => right
"**Robo** Das `∧` in der Annahme kannst Du mit `rcases h with ⟨h₁, h₂⟩` zerlegen." rcases h with ⟨h₁, h₂⟩
assumption
HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : B ∧ C) : (A C) => rcases h with ⟨h₁, h₂⟩
"**Robo** Das `∧` in der Annahme kannst Du mit `rcases {h} with ⟨h₁, h₂⟩` zerlegen." right
assumption
-- TODO: Hint nur Anhand der Annahmen? · Branch
right
HiddenHint (A : Prop) (B : Prop) : A B => rcases h with ⟨h₁, h₂⟩
"**Robo** `left` oder `right`?" assumption
rcases h with ⟨h₁, h₂⟩
right
assumption
rcases h with ⟨h₁, h₂⟩
constructor
· right
assumption
· right
assumption
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : B ∧ C) : (A B) ∧ (A C) =>
-- "**Robo** Das `∧` in der Annahme kannst Du mit `rcases {h} with ⟨h₁, h₂⟩` zerlegen."
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) : (A B) ∧ (A C) =>
-- "**Robo** Das `∧` im Goal kannst Du mit `constructor` zerlegen."
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : A (B ∧ C)) : (A B) ∧ (A C) =>
-- "**Robo** Das `` in der Annahme kannst Du mit `rcases h with h | h` zerlegen."
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : A (B ∧ C)) : (A B) =>
-- "**Robo** Das `` in der Annahme kannst Du mit `rcases h with h | h` zerlegen."
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : A (B ∧ C)) : (A C) =>
-- "**Robo** Das `` in der Annahme kannst Du mit `rcases h with h | h` zerlegen."
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : B ∧ C) : (A B) =>
-- "**Robo** Das `∧` in der Annahme kannst Du mit `rcases h with ⟨h₁, h₂⟩` zerlegen."
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : B ∧ C) : (A C) =>
-- "**Robo** Das `∧` in der Annahme kannst Du mit `rcases {h} with ⟨h₁, h₂⟩` zerlegen."
-- -- TODO: Hint nur Anhand der Annahmen?
-- HiddenHint (A : Prop) (B : Prop) : A B =>
-- "**Robo** `left` oder `right`?"
Conclusion Conclusion
" "
@ -74,5 +113,5 @@ Conclusion
Königin *Logisinde* ist in der Zwischenzeit eingeschlafen, und ihr stehlt euch heimlich davon. Königin *Logisinde* ist in der Zwischenzeit eingeschlafen, und ihr stehlt euch heimlich davon.
" "
NewTactics left right assumption constructor rcases rfl contradiction trivial NewTactic left right assumption constructor rcases rfl contradiction trivial
DisabledTactics tauto DisabledTactic tauto

@ -23,6 +23,21 @@ Game "TestGame"
World "SetTheory" World "SetTheory"
Title "Mengenlehre" Title "Mengenlehre"
Introduction
"Der größere der beiden Monde sieht dunkelrot und karg aus. Trotzdem sollen dort nomadische
Gesellschaften wohnen, die sich in der Einöde zurechtfinden.
Ihr steuert einen der wenigen befestigten Standorte am Fusse eines Berges an.
**Robo**: Die Bevölkerung hier lebt so abgekapselt vom Rest des Universums, dass sie sich
vermutlich noch viel besser mit alter Sprache und Schrift auskennt.
**Du**: Hoffen wir, dass sie uns weiterhelfen können dieses Buch der Urbilder zu entschlüsseln.
Sofort begrüßt euch eine ältere Frau, die sich als *Mengitte*, die Beschützerin des Mondes,
vorstellt.
"
Game "TestGame" Game "TestGame"
World "SetTheory2" World "SetTheory2"
Title "Mehr Mengenlehre" Title "Mehr Mengenlehre"

@ -14,29 +14,47 @@ Title "Mengen"
Introduction Introduction
" "
In diesem Kapitel schauen wir uns Mengen an. **Mengitte**: Ich würde leider den Inhalt jenes Buches eh nicht verstehen. Aber der beste Weg für
euch, dieses zu entschlüsseln ist, euch ausgiebig mit der Bevölkerung hier zu unterhalten.
Lebt mit ihnen, redet mit ihnen und ihr werdet die Sprache automatisch lernen.
Zuerst ein ganz wichtiger Punkt: Alle Mengen in Lean sind homogen. Das heisst, **Mengitte**: Seit aber vorgewarnt, die Leute hier denken ganz viel über Mengen nach,
alle Elemente in einer Menge haben den gleichen Typ. womit sie immer *homogene Mengen* meinen. Eine Menge natürlicher Zahlen `{1, 4, 6}` ist
verständlich, aber sowas wie eine Menge `{(2 : ), {3, 1}, \"e\", (1 : )}` gibt es hier
einfach nicht. Punkt.
Zum Beispiel `{1, 4, 6}` ist eine Menge von natürlichen Zahlen. Aber man kann keine **Robo**: Als Kontext: Wenn `A` ein beliebiger `Type` ist, dann ist `(U : Set A)` eine Menge
Menge `{(2 : ), {3, 1}, \"e\", (1 : )}` definieren, da die Elemente unterschiedliche Typen haben. mit Elementen aus `A`
Für einen Typen `{X : Type*}` definiert damit also `set X` der Type aller Mengen mit Elementen aus **Mengitte**: Damit ich weiss, dass ihr euch grundsätzlich mit den Leuten austauschen könnt,
`X`. `set.univ` ist dann ganz `X` also Menge betrachtet, und es ist wichtig den Unterschied erklärt mir doch folgendes:
zu kennen: `(univ : set X)` und `(X : Typ*)` haben nicht den gleichen Typ und sind damit auch nicht
austauschbar!
Um zu beweisen, dass etwas in `univ` ist, kannst du verschiedenste deiner Taktiken anwenden,
zum Beispiel `tauto`.
" "
open Set open Set
Statement mem_univ Statement mem_univ "" {A : Type} (x : A) : x ∈ (univ : Set A) := by
"4 ist ein Element der Menge aller natürlichen Zahlen." {A : Type _} (x : A) :
x ∈ (univ : Set A) := by
trivial trivial
-- tauto
NewTactics tauto trivial Hint (A : Type) (x : A) : x ∈ (univ : Set A) =>
"**Du**: Also `A` ist ein `Type`, `x` ist ein Element in `A`…
**Robo** … und `univ` ist die Menge aller Elemente in `A`.
**Du** ist das nicht einfach `A` selber?
**Robo** Fast, aber das eine ist ein `Type`, das andere eine Menge, also vom Typ `Set A`.
**Du**: Unlogisch.
**Mengites**: Naja, Typen und Mengen sind halt zwei unterschiedliche Sachen und wenn ihr
über Mengen sprechen wollt, müssen alles Mengen sein.
**Du**: Na gut. Und wieso `x ∈ univ` und nicht `x : univ` wie bei Typen?
**Robo**: Jedes Element `(x : A)` hat entweder die Eigenschaft `x ∈ U` oder `x ∉ U` für eine
Menge `(U : Set A)`. (`\\in`, `\\nin`)
**Du**: Also das ist ja dann trivial. Hoffentlich sehen die das hier auch so…
"
Conclusion "**Mengitte**: Ja das stimmt schon. Dann wünsche ich euch viel Erfolg auf eurer Reise!"

@ -13,17 +13,22 @@ Title "leere Menge"
Introduction Introduction
" "
Gleich wie bei `univ` gibt es leere Mengen `∅` von verschiedenen Typen. Ihr zieht also durch die Gegend und redet mit den Leuten. Ein Junge rennt zu euch und fragt:
So ist `(∅ : Set )` in Lean nicht das gleiche wie `(∅ : Set )`. (`\\empty`)
Zudem hat die Verneinung `¬ (x ∈ A)` die Notation `x ∉ A` (`\\nin`), gleich wie bei `=` and `≠`.
Um zu zeigen, dass etwas nicht in der leeren Menge ist, kannst du wieder `tauto` verwenden.
" "
open Set open Set
Statement not_mem_empty Statement not_mem_empty "" {A : Type} (x : A) :
"Kein Element ist in der leeren Menge enthalten." {A : Type _} (x : A) :
x ∉ (∅ : Set A) := by x ∉ (∅ : Set A) := by
tauto tauto
NewLemma mem_univ
Hint (A : Type) (x : A) : x ∉ (∅ : Set A) =>
"**Du**: Kein Element ist in der leeren Menge enthalten? Das ist ja alles tautologisches Zeugs...
**Robo**: Dann behaupte das doch. (`\\empty`)"
Conclusion "Der Junge rennt weiter.
**Du**: So wird das ganze schon angenehmer."

@ -28,18 +28,18 @@ namespace MySet
open Set open Set
theorem mem_univ {A : Type _} (x : A) : x ∈ (univ : Set A) := by -- theorem mem_univ {A : Type _} (x : A) : x ∈ (univ : Set A) := by
trivial -- trivial
theorem not_mem_empty {A : Type _} (x : A) : x ∉ (∅ : Set A) := by -- theorem not_mem_empty {A : Type _} (x : A) : x ∉ (∅ : Set A) := by
tauto -- tauto
Statement subset_empty_iff Statement subset_empty_iff
"." (A : Set ) : A ⊆ univ := by "." (A : Set ) : A ⊆ univ := by
intro h hA intro h hA
apply mem_univ -- or `trivial`. trivial --apply mem_univ -- or `trivial`.
NewTactics intro trivial apply NewTactic intro trivial apply
-- blocked: tauto simp -- blocked: tauto simp
end MySet end MySet

@ -54,8 +54,8 @@ Statement subset_empty_iff
rcases a with ⟨h₁, h₂⟩ rcases a with ⟨h₁, h₂⟩
assumption assumption
NewTactics constructor intro rw assumption rcases simp tauto trivial NewTactic constructor intro rw assumption rcases simp tauto trivial
NewLemmas Subset.antisymm_iff empty_subset NewLemma Subset.antisymm_iff empty_subset
end MySet end MySet

@ -45,8 +45,8 @@ Statement eq_empty_iff_forall_not_mem
rw [←subset_empty_iff] rw [←subset_empty_iff]
rfl -- This is quite a miracle :) rfl -- This is quite a miracle :)
NewTactics constructor intro rw assumption rcases simp tauto trivial NewTactic constructor intro rw assumption rcases simp tauto trivial
NewLemmas Subset.antisymm_iff empty_subset NewLemma Subset.antisymm_iff empty_subset
end MySet end MySet

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save