merge necessary as upstream developed while storyline slept

pull/53/head
Marcus Zibrowius 3 years ago
commit 71904d5460

@ -5,11 +5,15 @@ cd $(dirname $0)
cd server cd server
cd testgame cd adam
lake update lake update
cp lake-packages/mathlib/lean-toolchain lean-toolchain cp lake-packages/mathlib/lean-toolchain lean-toolchain
cp lake-packages/mathlib/lean-toolchain ../leanserver/lean-toolchain cp lake-packages/mathlib/lean-toolchain ../leanserver/lean-toolchain
cp lake-packages/mathlib/lean-toolchain ../nng/lean-toolchain
cd ../leanserver cd ../leanserver
lake update lake update
cd ../nng
lake update

@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { Outlet } from "react-router-dom"; import { Outlet, useParams } from "react-router-dom";
import '@fontsource/roboto/300.css'; import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css'; import '@fontsource/roboto/400.css';
@ -10,10 +10,15 @@ import '@fontsource/roboto/700.css';
import './reset.css'; import './reset.css';
import './app.css'; import './app.css';
export const GameIdContext = React.createContext<string>(undefined);
function App() { function App() {
const params = useParams();
return ( return (
<div className="app"> <div className="app">
<Outlet /> <GameIdContext.Provider value={params.gameId}>
<Outlet />
</GameIdContext.Provider>
</div> </div>
) )
} }

@ -5,6 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faLock, faLockOpen, faBook, faHammer, faBan } from '@fortawesome/free-solid-svg-icons' import { faLock, faLockOpen, faBook, faHammer, faBan } from '@fortawesome/free-solid-svg-icons'
import Markdown from './Markdown'; import Markdown from './Markdown';
import { useLoadDocQuery, ComputedInventoryItem } from '../state/api'; import { useLoadDocQuery, ComputedInventoryItem } from '../state/api';
import { GameIdContext } from '../App';
export function Inventory({ tactics, lemmas, definitions, setInventoryDoc } : export function Inventory({ tactics, lemmas, definitions, setInventoryDoc } :
{lemmas: ComputedInventoryItem[], {lemmas: ComputedInventoryItem[],
@ -55,14 +56,14 @@ function InventoryList({items, docType, openDoc} : {items: ComputedInventoryItem
).map(item => { ).map(item => {
if (tab == item.category) { if (tab == item.category) {
return <InventoryItem key={item.name} showDoc={() => {openDoc(item.name, docType)}} return <InventoryItem key={item.name} showDoc={() => {openDoc(item.name, docType)}}
name={item.name} locked={item.locked} disabled={item.disabled} /> name={item.name} displayName={item.displayName} locked={item.locked} disabled={item.disabled} />
} }
}) } }) }
</div> </div>
</> </>
} }
function InventoryItem({name, locked, disabled, showDoc}) { function InventoryItem({name, displayName, locked, disabled, showDoc}) {
const icon = locked ? <FontAwesomeIcon icon={faLock} /> : const icon = locked ? <FontAwesomeIcon icon={faLock} /> :
disabled ? <FontAwesomeIcon icon={faBan} /> : "" disabled ? <FontAwesomeIcon icon={faBan} /> : ""
const className = locked ? "locked" : disabled ? "disabled" : "" const className = locked ? "locked" : disabled ? "disabled" : ""
@ -73,15 +74,15 @@ function InventoryItem({name, locked, disabled, showDoc}) {
} }
} }
return <div className={`item ${className}`} onClick={handleClick}>{icon} {name}</div> return <div className={`item ${className}`} onClick={handleClick}>{icon} {displayName}</div>
} }
export function Documentation({name, type}) { export function Documentation({name, type}) {
const gameId = React.useContext(GameIdContext)
const doc = useLoadDocQuery({type: type, name: name}) const doc = useLoadDocQuery({game: gameId, type: type, name: name})
return <> return <>
<h2 className="doc">{doc.data?.name}</h2> <h2 className="doc">{doc.data?.displayName}</h2>
<Markdown>{doc.data?.text}</Markdown> <Markdown>{doc.data?.text}</Markdown>
</> </>
} }

@ -22,7 +22,7 @@ import './level.css'
import { Button } from './Button' import { Button } from './Button'
import { ConnectionContext, useLeanClient } from '../connection'; import { ConnectionContext, useLeanClient } from '../connection';
import { useGetGameInfoQuery, useLoadLevelQuery } from '../state/api'; import { useGetGameInfoQuery, useLoadLevelQuery } from '../state/api';
import { codeEdited, selectCode, progressSlice, selectCompleted } from '../state/progress'; import { changedSelection, codeEdited, selectCode, selectSelections, progressSlice, selectCompleted } from '../state/progress';
import { useAppDispatch, useAppSelector } from '../hooks'; import { useAppDispatch, useAppSelector } from '../hooks';
import { useStore } from 'react-redux'; import { useStore } from 'react-redux';
@ -40,6 +40,7 @@ import Markdown from './Markdown';
import Split from 'react-split' import Split from 'react-split'
import { Alert } from '@mui/material'; import { Alert } from '@mui/material';
import { GameIdContext } from '../App';
export const MonacoEditorContext = React.createContext<monaco.editor.IStandaloneCodeEditor>(null as any); export const MonacoEditorContext = React.createContext<monaco.editor.IStandaloneCodeEditor>(null as any);
@ -75,7 +76,9 @@ function PlayableLevel({worldId, levelId}) {
const codeviewRef = useRef<HTMLDivElement>(null) const codeviewRef = useRef<HTMLDivElement>(null)
const introductionPanelRef = useRef<HTMLDivElement>(null) const introductionPanelRef = useRef<HTMLDivElement>(null)
const initialCode = useAppSelector(selectCode(worldId, levelId)) const gameId = React.useContext(GameIdContext)
const initialCode = useAppSelector(selectCode(gameId, worldId, levelId))
const initialSelections = useAppSelector(selectSelections(gameId, worldId, levelId))
const [commandLineMode, setCommandLineMode] = useState(true) const [commandLineMode, setCommandLineMode] = useState(true)
const [commandLineInput, setCommandLineInput] = useState("") const [commandLineInput, setCommandLineInput] = useState("")
@ -86,6 +89,8 @@ function PlayableLevel({worldId, levelId}) {
useEffect(() => { useEffect(() => {
// Scroll to top when loading a new level // Scroll to top when loading a new level
introductionPanelRef.current!.scrollTo(0,0) introductionPanelRef.current!.scrollTo(0,0)
// Reset command line input when loading a new level
setCommandLineInput("")
}, [levelId]) }, [levelId])
React.useEffect(() => { React.useEffect(() => {
@ -122,24 +127,48 @@ function PlayableLevel({worldId, levelId}) {
}]); }]);
} }
const connection = React.useContext(ConnectionContext) const gameInfo = useGetGameInfoQuery({game: gameId})
const level = useLoadLevelQuery({game: gameId, world: worldId, level: levelId})
const gameInfo = useGetGameInfoQuery()
const level = useLoadLevelQuery({world: worldId, level: levelId})
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const onDidChangeContent = (code) => { const onDidChangeContent = (code) => {
dispatch(codeEdited({world: worldId, level: levelId, code})) dispatch(codeEdited({game: gameId, world: worldId, level: levelId, code}))
setCanUndo(code.trim() !== "") setCanUndo(code.trim() !== "")
} }
const completed = useAppSelector(selectCompleted(worldId, levelId)) const onDidChangeSelection = (monacoSelections) => {
const selections = monacoSelections.map(
({selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn}) =>
{return {selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn}})
dispatch(changedSelection({game: gameId, world: worldId, level: levelId, selections}))
}
const completed = useAppSelector(selectCompleted(gameId, worldId, levelId))
const {editor, infoProvider, editorConnection} = const {editor, infoProvider, editorConnection} =
useLevelEditor(worldId, levelId, codeviewRef, initialCode, onDidChangeContent) useLevelEditor(worldId, levelId, codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection)
// Effect when command line mode gets enabled
useEffect(() => {
if (editor && commandLineMode) {
let endPos = editor.getModel().getFullModelRange().getEndPosition()
if (editor.getModel().getLineContent(endPos.lineNumber).trim() !== "") {
editor.executeEdits("command-line", [{
range: monaco.Selection.fromPositions(endPos, endPos),
text: "\n",
forceMoveMarkers: true
}]);
}
endPos = editor.getModel().getFullModelRange().getEndPosition()
let currPos = editor.getPosition()
if (currPos.column != 1 || (currPos.lineNumber != endPos.lineNumber && currPos.lineNumber != endPos.lineNumber - 1)) {
// This is not a position that would naturally occur from CommandLine, reset:
editor.setSelection(monaco.Selection.fromPositions(endPos, endPos))
}
}
}, [editor, commandLineMode])
const [inventoryDoc, setInventoryDoc] = useState<{name: string, type: string}>(null) const [inventoryDoc, setInventoryDoc] = useState<{name: string, type: string}>(null)
@ -151,9 +180,9 @@ function PlayableLevel({worldId, levelId}) {
<Split minSize={0} snapOffset={200} sizes={[50, 25, 25]} className={`app-content level ${level.isLoading ? 'hidden' : ''}`}> <Split minSize={0} snapOffset={200} sizes={[50, 25, 25]} className={`app-content level ${level.isLoading ? 'hidden' : ''}`}>
<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 }}> <div className="message info">
<Markdown>{level?.data?.introduction}</Markdown> <Markdown>{level?.data?.introduction}</Markdown>
</Alert> </div>
</div> </div>
<div className="exercise"> <div className="exercise">
{/* <h4>Aufgabe:</h4> */} {/* <h4>Aufgabe:</h4> */}
@ -177,10 +206,12 @@ function PlayableLevel({worldId, levelId}) {
</EditorContext.Provider> </EditorContext.Provider>
{completed && <div className="conclusion"> {completed && <div className="conclusion">
<Markdown>{level?.data?.conclusion}</Markdown> <div className="message info">
<Markdown>{level?.data?.conclusion}</Markdown>
</div>
{levelId >= gameInfo.data?.worldSize[worldId] ? {levelId >= gameInfo.data?.worldSize[worldId] ?
<Button to={`/`}><FontAwesomeIcon icon={faHome} /></Button> : <Button to={`/game/${gameId}`}><FontAwesomeIcon icon={faHome} /></Button> :
<Button to={`/world/${worldId}/level/${levelId + 1}`}> <Button to={`/game/${gameId}/world/${worldId}/level/${levelId + 1}`}>
Next&nbsp;<FontAwesomeIcon icon={faArrowRight} /></Button>} Next&nbsp;<FontAwesomeIcon icon={faArrowRight} /></Button>}
</div>} </div>}
@ -200,23 +231,24 @@ function PlayableLevel({worldId, levelId}) {
export default Level export default Level
function Introduction({worldId}) { function Introduction({worldId}) {
const gameInfo = useGetGameInfoQuery() const gameId = React.useContext(GameIdContext)
const gameInfo = useGetGameInfoQuery({game: gameId})
return <> return <>
<div style={gameInfo.isLoading ? null : {display: "none"}} className="app-content loading"><CircularProgress /></div> <div style={gameInfo.isLoading ? null : {display: "none"}} className="app-content loading"><CircularProgress /></div>
<LevelAppBar isLoading={gameInfo.isLoading} levelTitle="Einführung" worldId={worldId} levelId={0} /> <LevelAppBar isLoading={gameInfo.isLoading} levelTitle="Einführung" worldId={worldId} levelId={0} />
<div style={gameInfo.isLoading ? {display: "none"} : null} className="exercise-panel"> <div style={gameInfo.isLoading ? {display: "none"} : null} className="exercise-panel">
<div className="introduction-panel"> <div className="introduction-panel">
<Alert severity="info" sx={{ mt: 1 }}> <div className="message info">
<Markdown> <Markdown>
{gameInfo.data?.worlds.nodes[worldId].introduction} {gameInfo.data?.worlds.nodes[worldId].introduction}
</Markdown> </Markdown>
</Alert> </div>
</div> </div>
<div className="conclusion"> <div className="conclusion">
{0 == gameInfo.data?.worldSize[worldId] ? {0 == gameInfo.data?.worldSize[worldId] ?
<Button to={`/`}><FontAwesomeIcon icon={faHome} /></Button> : <Button to={`/game/${gameId}`}><FontAwesomeIcon icon={faHome} /></Button> :
<Button to={`/world/${worldId}/level/1`}> <Button to={`/game/${gameId}/world/${worldId}/level/1`}>
Start&nbsp;<FontAwesomeIcon icon={faArrowRight} /> Start&nbsp;<FontAwesomeIcon icon={faArrowRight} />
</Button>} </Button>}
</div> </div>
@ -225,11 +257,12 @@ function Introduction({worldId}) {
} }
function LevelAppBar({isLoading, levelId, worldId, levelTitle}) { function LevelAppBar({isLoading, levelId, worldId, levelTitle}) {
const gameInfo = useGetGameInfoQuery() const gameId = React.useContext(GameIdContext)
const gameInfo = useGetGameInfoQuery({game: gameId})
return <div className="app-bar" style={isLoading ? {display: "none"} : null} > return <div className="app-bar" style={isLoading ? {display: "none"} : null} >
<div> <div>
<Button to={`/`}><FontAwesomeIcon icon={faHome} /></Button> <Button to={`/game/${gameId}`}><FontAwesomeIcon icon={faHome} /></Button>
<span className="app-bar-title"> <span className="app-bar-title">
{gameInfo.data?.worlds.nodes[worldId].title && `World: ${gameInfo.data?.worlds.nodes[worldId].title}`} {gameInfo.data?.worlds.nodes[worldId].title && `World: ${gameInfo.data?.worlds.nodes[worldId].title}`}
</span> </span>
@ -239,19 +272,20 @@ function LevelAppBar({isLoading, levelId, worldId, levelTitle}) {
{levelTitle} {levelTitle}
</span> </span>
<Button disabled={levelId <= 0} inverted={true} <Button disabled={levelId <= 0} inverted={true}
to={`/world/${worldId}/level/${levelId - 1}`} to={`/game/${gameId}/world/${worldId}/level/${levelId - 1}`}
><FontAwesomeIcon icon={faArrowLeft} />&nbsp;Previous</Button> ><FontAwesomeIcon icon={faArrowLeft} />&nbsp;Previous</Button>
<Button disabled={levelId >= gameInfo.data?.worldSize[worldId]} inverted={true} <Button disabled={levelId >= gameInfo.data?.worldSize[worldId]} inverted={true}
to={`/world/${worldId}/level/${levelId + 1}`} to={`/game/${gameId}/world/${worldId}/level/${levelId + 1}`}
>Next&nbsp;<FontAwesomeIcon icon={faArrowRight} /></Button> >Next&nbsp;<FontAwesomeIcon icon={faArrowRight} /></Button>
</div> </div>
</div> </div>
} }
function useLevelEditor(worldId: string, levelId: number, codeviewRef, initialCode, onDidChangeContent) { function useLevelEditor(worldId: string, levelId: number, codeviewRef, initialCode, initialSelections, onDidChangeContent, onDidChangeSelection) {
const connection = React.useContext(ConnectionContext) const connection = React.useContext(ConnectionContext)
const gameId = React.useContext(GameIdContext)
const [editor, setEditor] = useState<monaco.editor.IStandaloneCodeEditor|null>(null) const [editor, setEditor] = useState<monaco.editor.IStandaloneCodeEditor|null>(null)
const [infoProvider, setInfoProvider] = useState<null|InfoProvider>(null) const [infoProvider, setInfoProvider] = useState<null|InfoProvider>(null)
@ -278,7 +312,7 @@ function useLevelEditor(worldId: string, levelId: number, codeviewRef, initialCo
theme: 'vs-code-theme-converted' theme: 'vs-code-theme-converted'
}) })
const infoProvider = new InfoProvider(connection.getLeanClient()) const infoProvider = new InfoProvider(connection.getLeanClient(gameId))
const editorApi = infoProvider.getApi() const editorApi = infoProvider.getApi()
@ -328,7 +362,7 @@ function useLevelEditor(worldId: string, levelId: number, codeviewRef, initialCo
return () => { infoProvider.dispose(); editor.dispose() } return () => { infoProvider.dispose(); editor.dispose() }
}, []) }, [])
const {leanClient, leanClientStarted} = useLeanClient() const {leanClient, leanClientStarted} = useLeanClient(gameId)
// Create model when level changes // Create model when level changes
useEffect(() => { useEffect(() => {
@ -340,8 +374,11 @@ function useLevelEditor(worldId: string, levelId: number, codeviewRef, initialCo
model = monaco.editor.createModel(initialCode, 'lean4', uri) model = monaco.editor.createModel(initialCode, 'lean4', uri)
} }
model.onDidChangeContent(() => onDidChangeContent(model.getValue())) model.onDidChangeContent(() => onDidChangeContent(model.getValue()))
editor.onDidChangeCursorSelection(() => onDidChangeSelection(editor.getSelections()))
editor.setModel(model) editor.setModel(model)
editor.setPosition(model.getFullModelRange().getEndPosition()) if (initialSelections) {
editor.setSelections(initialSelections)
}
infoviewApi.serverRestarted(leanClient.initializeResult) infoviewApi.serverRestarted(leanClient.initializeResult)
infoProvider.openPreview(editor, infoviewApi) infoProvider.openPreview(editor, infoviewApi)
@ -358,7 +395,8 @@ function useLevelEditor(worldId: string, levelId: number, codeviewRef, initialCo
/** Open all files in this world on the server so that they will load faster when accessed */ /** Open all files in this world on the server so that they will load faster when accessed */
function useLoadWorldFiles(worldId) { function useLoadWorldFiles(worldId) {
const gameInfo = useGetGameInfoQuery() const gameId = React.useContext(GameIdContext)
const gameInfo = useGetGameInfoQuery({game: gameId})
const store = useStore() const store = useStore()
useEffect(() => { useEffect(() => {
@ -370,7 +408,7 @@ function useLoadWorldFiles(worldId) {
if (model) { if (model) {
models.push(model) models.push(model)
} else { } else {
const code = selectCode(worldId, levelId)(store.getState()) const code = selectCode(gameId, worldId, levelId)(store.getState())
models.push(monaco.editor.createModel(code, 'lean4', uri)) models.push(monaco.editor.createModel(code, 'lean4', uri))
} }
} }

@ -0,0 +1,52 @@
import { faShield } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import * as React from 'react'
const PrivacyPolicy: React.FC = () => {
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<span>
<div className="privacy" onClick={handleOpen} title="Privacy Policy &amp; Impressum">
<FontAwesomeIcon icon={faShield} />
<p className="p1">legal</p>
<p className="p2">notes</p>
</div>
{open?
<div className="modal-wrapper">
<div className="modal-backdrop" onClick={handleClose} />
<div className="modal">
<div className="codicon codicon-close modal-close" onClick={handleClose}></div>
<h2>Privacy Policy &amp; Impressum</h2>
<p>Our server collects metadata (such as IP address, browser, operating system)
and the data that the user enters into the editor. The data is used to
compute the Lean output and display it to the user. The information will be stored
as long as the user stays on our website and will be deleted immediately afterwards.
We keep logs to improve our software, but the contained data is anonymized.</p>
<p>We do not use cookies, but your game progress is stored in the browser
as site data. Your game progress is not saved on the server; if you delete
your browser storage, it is completely gone.
</p>
<p>Our server is located in Germany.</p>
<p><strong>Contact information:</strong><br />
Jon Eugster<br />
Mathematisches Institut der Heinrich-Heine-Universität Düsseldorf<br />
Universitätsstr. 1<br />
40225 Düsseldorf<br />
Germany<br />
<a href="mailto:jon.eugster@hhu.de">jon.eugster@hhu.de</a>
</p>
</div>
</div> : null}
</span>
)
}
export default PrivacyPolicy

@ -5,7 +5,9 @@ import cytoscape, { LayoutOptions } from 'cytoscape'
import klay from 'cytoscape-klay'; import klay from 'cytoscape-klay';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import Split from 'react-split'
import PrivacyPolicy from './PrivacyPolicy';
cytoscape.use( klay ); cytoscape.use( klay );
@ -14,14 +16,25 @@ import { useGetGameInfoQuery } from '../state/api';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import Markdown from './Markdown'; import Markdown from './Markdown';
import { selectCompleted } from '../state/progress'; import { selectCompleted } from '../state/progress';
import { GameIdContext } from '../App';
const N = 24 // max number of levels per world
const R = 800 // radius of a world
const r = 110 // radius of a level
const s = 100 // global scale
const padding = 2000 // padding of the graphic (on a different scale)
function LevelIcon({ worldId, levelId, position }) { function LevelIcon({ worldId, levelId, position }) {
const completed = useSelector(selectCompleted(worldId,levelId)) const gameId = React.useContext(GameIdContext)
const completed = useSelector(selectCompleted(gameId, worldId,levelId))
const x = s * position.x + Math.sin(levelId * 2 * Math.PI / N) * (R + 1.2*r + 2*Math.floor((levelId - 1)/N))
const y = s * position.y - Math.cos(levelId * 2 * Math.PI / N) * (R + 1.2*r + 2*Math.floor((levelId - 1)/N))
// TODO: relative positioning? // TODO: relative positioning?
return ( return (
<Link to={`/world/${worldId}/level/${levelId}`} key={`/world/${worldId}/level/${levelId}`}> <Link to={`/game/${gameId}/world/${worldId}/level/${levelId}`} key={`/game/${gameId}/world/${worldId}/level/${levelId}`}>
<circle fill={completed ? "green" :"#aaa"} cx={position.x + Math.sin(levelId/5) * 9} cy={position.y - Math.cos(levelId/5) * 9} r="0.8" /> <circle fill={completed ? "green" :"#999"} cx={x} cy={y} r={r} />
</Link> </Link>
) )
} }
@ -29,7 +42,8 @@ function LevelIcon({ worldId, levelId, position }) {
function Welcome() { function Welcome() {
const navigate = useNavigate(); const navigate = useNavigate();
const gameInfo = useGetGameInfoQuery() const gameId = React.useContext(GameIdContext)
const gameInfo = useGetGameInfoQuery({game: gameId})
const { nodes, bounds }: any = gameInfo.data ? computeWorldLayout(gameInfo.data?.worlds) : {nodes: []} const { nodes, bounds }: any = gameInfo.data ? computeWorldLayout(gameInfo.data?.worlds) : {nodes: []}
@ -39,56 +53,66 @@ function Welcome() {
} }
}, [gameInfo.data?.title]) }, [gameInfo.data?.title])
const padding = 20
const svgElements = [] const svgElements = []
if (gameInfo.data) { if (gameInfo.data) {
for (let i in gameInfo.data.worlds.edges) { for (let i in gameInfo.data.worlds.edges) {
const edge = gameInfo.data.worlds.edges[i] const edge = gameInfo.data.worlds.edges[i]
svgElements.push( svgElements.push(
<line key={`pathway${i}`} x1={nodes[edge[0]].position.x} y1={nodes[edge[0]].position.y} <line key={`pathway${i}`} x1={s*nodes[edge[0]].position.x} y1={s*nodes[edge[0]].position.y}
x2={nodes[edge[1]].position.x} y2={nodes[edge[1]].position.y} stroke="#1976d2" strokeWidth="1"/> x2={s*nodes[edge[1]].position.x} y2={s*nodes[edge[1]].position.y} stroke="#1976d2" strokeWidth={s}/>
) )
} }
for (let id in nodes) { for (let id in nodes) {
let position: cytoscape.Position = nodes[id].position let position: cytoscape.Position = nodes[id].position
svgElements.push(
<Link key={`world${id}`} to={`/world/${id}/level/0`}>
<circle className="world-circle" cx={position.x} cy={position.y} r="8" />
<text className="world-name"
x={position.x} y={position.y}>{nodes[id].data.title ? nodes[id].data.title : id}</text>
</Link>
)
for (let i = 1; i <= gameInfo.data.worldSize[id]; i++) { for (let i = 1; i <= gameInfo.data.worldSize[id]; i++) {
svgElements.push( svgElements.push(
<LevelIcon position={position} worldId={id} levelId={i} /> <LevelIcon position={position} worldId={id} levelId={i} />
) )
} }
svgElements.push(
<Link key={`world${id}`} to={`/game/${gameId}/world/${id}/level/0`}>
<circle className="world-circle" cx={s*position.x} cy={s*position.y} r={R}
fill="#1976d2"/>
<foreignObject className="world-title-wrapper" x={s*position.x} y={s*position.y}
width={1.42*R} height={1.42*R} transform={"translate("+ -.71*R +","+ -.71*R +")"}>
<div>
<p className="world-title" style={{fontSize: Math.floor(R/4) + "px"}}>
{nodes[id].data.title ? nodes[id].data.title : id}
</p>
</div>
</foreignObject>
</Link>
)
} }
} }
return <div> return <div className="app-content ">
{ gameInfo.isLoading? { gameInfo.isLoading?
<Box display="flex" alignItems="center" justifyContent="center" sx={{ height: "calc(100vh - 64px)" }}><CircularProgress /></Box> <Box display="flex" alignItems="center" justifyContent="center" sx={{ height: "calc(100vh - 64px)" }}>
<CircularProgress />
</Box>
: :
<div className="app-content"> <Split className="welcome" minSize={200} sizes={[70, 30]}>
<Box sx={{ m: 3 }}> <div className="column">
<Typography variant="body1" component="div"> <Typography variant="body1" component="div" className="welcome-text">
<Markdown>{gameInfo.data?.introduction}</Markdown> <Markdown>{gameInfo.data?.introduction}</Markdown>
</Typography> </Typography>
</Box> </div>
<Box textAlign='center' sx={{ m: 5 }}> <div className="column">
<svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="30%" <Box textAlign='center' sx={{ m: 5 }}>
viewBox={bounds ? `${bounds.x1 - padding} ${bounds.y1 - padding} ${bounds.x2 - bounds.x1 + 2 * padding} ${bounds.y2 - bounds.y1 + 2 * padding}` : ''}> <svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink"
{svgElements} viewBox={bounds ? `${s*bounds.x1 - padding} ${s*bounds.y1 - padding} ${s*bounds.x2 - s*bounds.x1 + 2 * padding} ${s*bounds.y2 - s*bounds.y1 + 2 * padding}` : ''}>
</svg> {svgElements}
</Box> </svg>
</div> </Box>
</div>
</Split>
} }
<PrivacyPolicy/>
</div> </div>
} }
@ -116,9 +140,8 @@ function computeWorldLayout(worlds) {
headless: true, headless: true,
styleEnabled: false styleEnabled: false
}) })
// TODO: Jon play around with graph layout
const layout = cy.layout({name: "klay", klay: {direction: "DOWN"}} as LayoutOptions).run() const layout = cy.layout({name: "klay", klay: {direction: "DOWN", nodePlacement: "LINEAR_SEGMENTS"}} as LayoutOptions).run()
let nodes = {} let nodes = {}
cy.nodes().forEach((node, id) => { cy.nodes().forEach((node, id) => {
nodes[node.id()] = { nodes[node.id()] = {

@ -170,21 +170,6 @@ export function CommandLine() {
return () => { l.dispose() } return () => { l.dispose() }
}, [oneLineEditor, runCommand]) }, [oneLineEditor, runCommand])
// Effect when command line mode gets enabled
useEffect(() => {
if (commandLineMode) {
const endPos = editor.getModel().getFullModelRange().getEndPosition()
if (editor.getModel().getLineContent(endPos.lineNumber).trim() !== "") {
editor.executeEdits("command-line", [{
range: monaco.Selection.fromPositions(endPos, endPos),
text: commandLineInput + "\n",
forceMoveMarkers: false
}]);
}
editor.setPosition(editor.getModel().getFullModelRange().getEndPosition())
}
}, [commandLineMode])
const handleSubmit : React.FormEventHandler<HTMLFormElement> = (ev) => { const handleSubmit : React.FormEventHandler<HTMLFormElement> = (ev) => {
ev.preventDefault() ev.preventDefault()
runCommand() runCommand()

@ -4,7 +4,7 @@ import { Alert, FormControlLabel, Switch } from '@mui/material';
import Markdown from '../Markdown'; import Markdown from '../Markdown';
function Hint({hint} : {hint: GameHint}) { function Hint({hint} : {hint: GameHint}) {
return <Alert severity="info" sx={{ mt: 1 }}><Markdown>{hint.text}</Markdown></Alert> return <div className="message info"><Markdown>{hint.text}</Markdown></div>
} }
export function Hints({hints} : {hints: GameHint[]}) { export function Hints({hints} : {hints: GameHint[]}) {

@ -4,9 +4,9 @@
padding: 5px 10px; padding: 5px 10px;
border-radius: 3px 3px 3px 3px; border-radius: 3px 3px 3px 3px;
} }
.message.information { .message.info {
color: #059; /* color: #059; */
background-color: #BEF; background-color: #DDF6FF;
} }
.message.warning { .message.warning {
color: #9F6000; color: #9F6000;

@ -18,11 +18,13 @@ import { EditorContext, ConfigContext, ProgressContext, VersionContext } from '.
import { WithRpcSessions } from '../../../../node_modules/lean4-infoview/src/infoview/rpcSessions'; import { WithRpcSessions } from '../../../../node_modules/lean4-infoview/src/infoview/rpcSessions';
import { ServerVersion } from '../../../../node_modules/lean4-infoview/src/infoview/serverVersion'; import { ServerVersion } from '../../../../node_modules/lean4-infoview/src/infoview/serverVersion';
import { useAppDispatch, useAppSelector } from '../../hooks'; import { useAppDispatch, useAppSelector } from '../../hooks';
import { codeEdited, levelCompleted, selectCompleted } from '../../state/progress'; import { levelCompleted, selectCompleted } from '../../state/progress';
import { GameIdContext } from '../../App';
export function Main(props: {world: string, level: number}) { export function Main(props: {world: string, level: number}) {
const ec = React.useContext(EditorContext); const ec = React.useContext(EditorContext);
const gameId = React.useContext(GameIdContext)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
@ -33,13 +35,13 @@ export function Main(props: {world: string, level: number}) {
if (ec.events.changedCursorLocation.current && if (ec.events.changedCursorLocation.current &&
ec.events.changedCursorLocation.current.uri === params.uri) { ec.events.changedCursorLocation.current.uri === params.uri) {
dispatch(levelCompleted({world: props.world, level: props.level})) dispatch(levelCompleted({game: gameId, world: props.world, level: props.level}))
} }
}, },
[] []
); );
const completed = useAppSelector(selectCompleted(props.world, props.level)) const completed = useAppSelector(selectCompleted(gameId, props.world, props.level))
/* Set up updates to the global infoview state on editor events. */ /* Set up updates to the global infoview state on editor events. */
const config = useEventResult(ec.events.changedInfoviewConfig) ?? defaultInfoviewConfig; const config = useEventResult(ec.events.changedInfoviewConfig) ?? defaultInfoviewConfig;

@ -1,12 +1,191 @@
svg .world-circle { /* svg .world-circle {
fill: var(--clr-primary) fill: var(--clr-primary)
} */
.welcome {
height: 100%;
flex: 1;
min-height: 0;
display: flex;
}
.app-content {
height: 100%
}
.welcome .column {
height: 100%;
overflow: auto;
}
.welcome-text {
padding: 20px;
}
h1 {
font-size: 2em;
margin: .67em 0;
}
h2 {
font-size: 1.5em;
}
h3 {
font-size: 1.3em;
}
h4 {
font-size: 1.1em;
font-style: italic;
}
h5, h6 {
font-size: 1em;
font-style: italic;
}
/***************/
/* SVG Graphic */
/***************/
svg .world-title-wrapper {
overflow: auto;
}
svg .world-title-wrapper div {
width: 100%;
height: 100%;
}
svg .world-title-wrapper div {
display: flex;
align-items:center;
justify-content:center;
overflow: visible;
} }
svg .world-name { svg .world-title {
fill: white;
font-size: 2px;
font-weight: 500; font-weight: 500;
text-anchor: middle; color: white;
dominant-baseline: middle; margin: 0;
padding: 0;
}
/******************/
/* Privacy Button */
/******************/
.privacy {
width: 40px;
height: 40px;
font-size: 25px;
border-radius: 20px;
position: absolute;
right: 10px;
bottom: 10px;
display: flex;
align-items:center;
justify-content:center;
color: #aaa;
background-color: #eee;
cursor: pointer;
}
.privacy p {
position: absolute;
color: #888;
bottom: 1.5px;
font-size: 6px;
}
.privacy .p1 {
transform: rotate(50deg);
left: 1.5px;
}
.privacy .p2 {
transform: rotate(-50deg);
right: 1.5px;
}
/*****************/
/* Privacy Popup */
/*****************/
.modal-wrapper {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
padding: 0;
}
.modal-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, 0.25);
z-index: 2;
}
.modal h2 {
text-align: center;
}
.modal {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
min-width: 50%;
max-width: 60ch;
background: #fff;
z-index: 3;
padding: 2em;
border-radius: 1em;
text-align: left;
color: var(--vscode-breadcrumb-foreground);
}
.modal input[type="text"] {
width: 100%;
}
.modal .form-error {
color: #a00;
font-weight: bold;
}
.modal input[type="submit"] {
border: none;
color: var(--vscode-button-foreground);
background: var(--vscode-button-background);
cursor: pointer;
padding: .5em 1em;
border-radius: .2em;
display: block;
margin: 1em auto;
}
.modal-close {
float: right;
scale: 2;
color: var(--vscode-breadcrumb-foreground);
cursor: pointer;
}
.modal-close:hover {
float: right;
scale: 2;
color: var(--vscode-breadcrumb-focusForeground);
}
.modal table {
width: 100%;
} }

@ -6,12 +6,17 @@ import { useState } from 'react';
export class Connection { export class Connection {
private leanClient = null private game: string = undefined // We only keep a connection to a single game at a time
private leanClient: LeanClient = null
getLeanClient(): LeanClient {
if (this.leanClient === null) {
const socketUrl = ((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + '/websocket/'
getLeanClient(game): LeanClient {
if (this.game !== game) {
if (this.leanClient) {
this.leanClient.stop() // Stop previous Lean client
}
this.game = game
// Start a new Lean client for the new `gameId`.
const socketUrl = ((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + '/websocket/' + game
const uri = monaco.Uri.parse('file:///') const uri = monaco.Uri.parse('file:///')
this.leanClient = new LeanClient(socketUrl, undefined, uri, () => {}) this.leanClient = new LeanClient(socketUrl, undefined, uri, () => {})
} }
@ -22,9 +27,9 @@ export class Connection {
/** If not already started, starts the Lean client. resolves the returned promise as soon as a /** If not already started, starts the Lean client. resolves the returned promise as soon as a
* Lean client is running. * Lean client is running.
*/ */
startLeanClient = () => { startLeanClient = (game) => {
return new Promise<LeanClient>((resolve) => { return new Promise<LeanClient>((resolve) => {
const leanClient = this.getLeanClient() const leanClient = this.getLeanClient(game)
if (leanClient.isRunning()) { if (leanClient.isRunning()) {
resolve(leanClient) resolve(leanClient)
} else { } else {
@ -47,8 +52,8 @@ export const connection = new Connection()
export const ConnectionContext = React.createContext(null); export const ConnectionContext = React.createContext(null);
export const useLeanClient = () => { export const useLeanClient = (gameId) => {
const leanClient = connection.getLeanClient() const leanClient = connection.getLeanClient(gameId)
const [leanClientStarted, setLeanClientStarted] = useState(leanClient.isStarted()) const [leanClientStarted, setLeanClientStarted] = useState(leanClient.isStarted())
React.useEffect(() => { React.useEffect(() => {

@ -13,21 +13,26 @@ import ErrorPage from './ErrorPage';
import Welcome from './components/Welcome'; import Welcome from './components/Welcome';
import Level from './components/Level'; import Level from './components/Level';
import { monacoSetup } from 'lean4web/client/src/monacoSetup'; import { monacoSetup } from 'lean4web/client/src/monacoSetup';
import { redirect } from 'react-router-dom';
monacoSetup() monacoSetup()
const router = createHashRouter([ const router = createHashRouter([
{ {
path: "/", path: "/",
loader: () => redirect("/game/adam")
},
{
path: "/game/:gameId",
element: <App />, element: <App />,
errorElement: <ErrorPage />, errorElement: <ErrorPage />,
children: [ children: [
{ {
path: "/", path: "/game/:gameId",
element: <Welcome />, element: <Welcome />,
}, },
{ {
path: "/world/:worldId/level/:levelId", path: "/game/:gameId/world/:worldId/level/:levelId",
element: <Level />, element: <Level />,
}, },
], ],

@ -12,6 +12,7 @@ interface GameInfo {
export interface ComputedInventoryItem { export interface ComputedInventoryItem {
name: string, name: string,
displayName: string,
category: string, category: string,
disabled: boolean, disabled: boolean,
locked: boolean locked: boolean
@ -31,18 +32,19 @@ interface LevelInfo {
interface Doc { interface Doc {
name: string, name: string,
displayName: string,
text: string text: string
} }
const customBaseQuery = async ( const customBaseQuery = async (
args : {method: string, params?: any}, args : {game: string, method: string, params?: any},
{ signal, dispatch, getState, extra }, { signal, dispatch, getState, extra },
extraOptions extraOptions
) => { ) => {
try { try {
const connection : Connection = extra.connection const connection : Connection = extra.connection
let leanClient = await connection.startLeanClient() let leanClient = await connection.startLeanClient(args.game)
console.log(`Sending request ${args.method}`) console.log(`Sending request ${args.method}`)
let res = await leanClient.sendRequest(args.method, args.params) let res = await leanClient.sendRequest(args.method, args.params)
console.log('Received response', res) console.log('Received response', res)
@ -57,14 +59,14 @@ export const apiSlice = createApi({
reducerPath: 'gameApi', reducerPath: 'gameApi',
baseQuery: customBaseQuery, baseQuery: customBaseQuery,
endpoints: (builder) => ({ endpoints: (builder) => ({
getGameInfo: builder.query<GameInfo, void>({ getGameInfo: builder.query<GameInfo, {game: string}>({
query: () => {return {method: 'info', params: {}}}, query: ({game}) => {return {game, method: 'info', params: {}}},
}), }),
loadLevel: builder.query<LevelInfo, {world: string, level: number}>({ loadLevel: builder.query<LevelInfo, {game: string, world: string, level: number}>({
query: ({world, level}) => {return {method: "loadLevel", params: {world, level}}}, query: ({game, world, level}) => {return {game, method: "loadLevel", params: {world, level}}},
}), }),
loadDoc: builder.query<Doc, {name: string, type: "lemma"|"tactic"}>({ loadDoc: builder.query<Doc, {game: string, name: string, type: "lemma"|"tactic"}>({
query: ({name, type}) => {return {method: "loadDoc", params: {name, type}}}, query: ({game, name, type}) => {return {game, method: "loadDoc", params: {name, type}}},
}), }),
}), }),
}) })

@ -1,4 +1,4 @@
const KEY = "progress"; const KEY = "game_progress";
export function loadState() { export function loadState() {
try { try {
const serializedState = localStorage.getItem(KEY); const serializedState = localStorage.getItem(KEY);

@ -3,23 +3,32 @@ import type { PayloadAction } from '@reduxjs/toolkit'
import { loadState } from "./localStorage"; import { loadState } from "./localStorage";
interface ProgressState { interface ProgressState {
level: {[world: string]: {[level: number]: LevelProgressState}} level: {[game: string]: {[world: string]: {[level: number]: LevelProgressState}}}
}
interface Selection {
selectionStartLineNumber: number,
selectionStartColumn: number,
positionLineNumber: number
positionColumn: number
} }
interface LevelProgressState { interface LevelProgressState {
code: string, code: string,
selections: Selection[],
completed: boolean completed: boolean
} }
const initialProgressState = loadState() ?? { level: {} } as ProgressState const initialProgressState = loadState() ?? { level: {} } as ProgressState
const initalLevelProgressState = {code: "", completed: false} as LevelProgressState const initalLevelProgressState = {code: "", completed: false} as LevelProgressState
function addLevelProgress(state, action: PayloadAction<{world: string, level: number}>) { function addLevelProgress(state, action: PayloadAction<{game: string, world: string, level: number}>) {
if (!state.level[action.payload.world]) { if (!state.level[action.payload.game]) {
state.level[action.payload.world] = {} state.level[action.payload.game] = {}
}
if (!state.level[action.payload.game][action.payload.world]) {
state.level[action.payload.game][action.payload.world] = {}
} }
if (!state.level[action.payload.world][action.payload.level]) { if (!state.level[action.payload.game][action.payload.world][action.payload.level]) {
state.level[action.payload.world][action.payload.level] = {...initalLevelProgressState} state.level[action.payload.game][action.payload.world][action.payload.level] = {...initalLevelProgressState}
} }
} }
@ -27,35 +36,46 @@ export const progressSlice = createSlice({
name: 'progress', name: 'progress',
initialState: initialProgressState, initialState: initialProgressState,
reducers: { reducers: {
codeEdited(state, action: PayloadAction<{world: string, level: number, code: string}>) { codeEdited(state, action: PayloadAction<{game: string, world: string, level: number, code: string}>) {
addLevelProgress(state, action)
state.level[action.payload.game][action.payload.world][action.payload.level].code = action.payload.code
state.level[action.payload.game][action.payload.world][action.payload.level].completed = false
},
changedSelection(state, action: PayloadAction<{game: string, world: string, level: number, selections: Selection[]}>) {
addLevelProgress(state, action) addLevelProgress(state, action)
state.level[action.payload.world][action.payload.level].code = action.payload.code state.level[action.payload.game][action.payload.world][action.payload.level].selections = action.payload.selections
state.level[action.payload.world][action.payload.level].completed = false
}, },
levelCompleted(state, action: PayloadAction<{world: string, level: number}>) { levelCompleted(state, action: PayloadAction<{game: string, world: string, level: number}>) {
addLevelProgress(state, action) addLevelProgress(state, action)
state.level[action.payload.world][action.payload.level].completed = true state.level[action.payload.game][action.payload.world][action.payload.level].completed = true
}, },
} }
}) })
export function selectLevel(world: string, level: number) { export function selectLevel(game: string, world: string, level: number) {
return (state) =>{ return (state) =>{
if (!state.progress.level[world]) { return initalLevelProgressState } if (!state.progress.level[game]) { return initalLevelProgressState }
if (!state.progress.level[world][level]) { return initalLevelProgressState } if (!state.progress.level[game][world]) { return initalLevelProgressState }
return state.progress.level[world][level] if (!state.progress.level[game][world][level]) { return initalLevelProgressState }
return state.progress.level[game][world][level]
}
}
export function selectCode(game: string, world: string, level: number) {
return (state) => {
return selectLevel(game, world, level)(state).code
} }
} }
export function selectCode(world: string, level: number) { export function selectSelections(game: string, world: string, level: number) {
return (state) => { return (state) => {
return selectLevel(world, level)(state).code return selectLevel(game, world, level)(state).selections
} }
} }
export function selectCompleted(world: string, level: number) { export function selectCompleted(game: string, world: string, level: number) {
return (state) => { return (state) => {
return selectLevel(world, level)(state).completed return selectLevel(game, world, level)(state).completed
} }
} }
@ -65,4 +85,4 @@ export function selectProgress() {
} }
} }
export const { codeEdited, levelCompleted } = progressSlice.actions export const { changedSelection, codeEdited, levelCompleted } = progressSlice.actions

23
package-lock.json generated

@ -18,6 +18,7 @@
"@types/cytoscape": "^3.19.9", "@types/cytoscape": "^3.19.9",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"cytoscape": "^3.23.0", "cytoscape": "^3.23.0",
"cytoscape-elk": "^2.1.0",
"cytoscape-klay": "^3.1.4", "cytoscape-klay": "^3.1.4",
"debounce": "^1.2.1", "debounce": "^1.2.1",
"express": "^4.18.2", "express": "^4.18.2",
@ -34,6 +35,7 @@
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"remark-math": "^5.1.1", "remark-math": "^5.1.1",
"vscode-ws-jsonrpc": "^2.0.1", "vscode-ws-jsonrpc": "^2.0.1",
"web-worker": "^1.2.0",
"ws": "^8.11.0" "ws": "^8.11.0"
}, },
"devDependencies": { "devDependencies": {
@ -4180,6 +4182,17 @@
"node": ">=0.10" "node": ">=0.10"
} }
}, },
"node_modules/cytoscape-elk": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cytoscape-elk/-/cytoscape-elk-2.1.0.tgz",
"integrity": "sha512-stkKoUTNOqpyP5eMuqatK0EYir2NWGTH+XlY0rxFj0t0HiQPGI4AuSuTPaGbNM1WhVfb0tWJ5TQQ0R0qshACLw==",
"dependencies": {
"elkjs": "^0.8.1"
},
"peerDependencies": {
"cytoscape": "^3.2.0"
}
},
"node_modules/cytoscape-klay": { "node_modules/cytoscape-klay": {
"version": "3.1.4", "version": "3.1.4",
"resolved": "https://registry.npmjs.org/cytoscape-klay/-/cytoscape-klay-3.1.4.tgz", "resolved": "https://registry.npmjs.org/cytoscape-klay/-/cytoscape-klay-3.1.4.tgz",
@ -4435,6 +4448,11 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.286.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.286.tgz",
"integrity": "sha512-Vp3CVhmYpgf4iXNKAucoQUDcCrBQX3XLBtwgFqP9BUXuucgvAV9zWp1kYU7LL9j4++s9O+12cb3wMtN4SJy6UQ==" "integrity": "sha512-Vp3CVhmYpgf4iXNKAucoQUDcCrBQX3XLBtwgFqP9BUXuucgvAV9zWp1kYU7LL9j4++s9O+12cb3wMtN4SJy6UQ=="
}, },
"node_modules/elkjs": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz",
"integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ=="
},
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@ -9869,6 +9887,11 @@
"url": "https://github.com/sponsors/wooorm" "url": "https://github.com/sponsors/wooorm"
} }
}, },
"node_modules/web-worker": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz",
"integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA=="
},
"node_modules/webpack": { "node_modules/webpack": {
"version": "5.75.0", "version": "5.75.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",

@ -14,6 +14,7 @@
"@types/cytoscape": "^3.19.9", "@types/cytoscape": "^3.19.9",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"cytoscape": "^3.23.0", "cytoscape": "^3.23.0",
"cytoscape-elk": "^2.1.0",
"cytoscape-klay": "^3.1.4", "cytoscape-klay": "^3.1.4",
"debounce": "^1.2.1", "debounce": "^1.2.1",
"express": "^4.18.2", "express": "^4.18.2",
@ -30,6 +31,7 @@
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"remark-math": "^5.1.1", "remark-math": "^5.1.1",
"vscode-ws-jsonrpc": "^2.0.1", "vscode-ws-jsonrpc": "^2.0.1",
"web-worker": "^1.2.0",
"ws": "^8.11.0" "ws": "^8.11.0"
}, },
"devDependencies": { "devDependencies": {
@ -56,7 +58,7 @@
}, },
"scripts": { "scripts": {
"start": "concurrently -n server,client -c blue,green \"npm run start_server\" \"npm run start_client\"", "start": "concurrently -n server,client -c blue,green \"npm run start_server\" \"npm run start_client\"",
"start_server": "cd server && (cd leanserver && lake build) && (cd testgame && lake exe cache get && lake build) && NODE_ENV=development nodemon -e mjs --exec \"node ./index.mjs\"", "start_server": "cd server && (cd leanserver && lake build) && (cd adam && lake exe cache get && lake build) && (cd nng && lake build) && NODE_ENV=development nodemon -e mjs --exec \"node ./index.mjs\"",
"start_client": "NODE_ENV=development webpack-dev-server --hot", "start_client": "NODE_ENV=development webpack-dev-server --hot",
"build": "npm run build_server && npm run build_client", "build": "npm run build_server && npm run build_client",
"build_server": "server/build.sh", "build_server": "server/build.sh",

@ -0,0 +1,44 @@
import Adam.Metadata
import Adam.Levels.Proposition
import Adam.Levels.Implication
import Adam.Levels.Predicate
import Adam.Levels.Contradiction
-- import Adam.Levels.Prime
import Adam.Levels.Sum
-- import Adam.Levels.Induction
import Adam.Levels.Numbers
import Adam.Levels.Inequality
import Adam.Levels.Lean
import Adam.Levels.SetTheory
import Adam.Levels.Function
import Adam.Levels.SetFunction
import Adam.Levels.LinearAlgebra
Game "Adam"
Title "Lean 4 game"
Introduction
"
"
Conclusion
"Fertig!"
Path Proposition → Implication → Predicate → Predicate → Contradiction → Sum → Lean
Path Predicate → Inequality → Sum
-- Path Inequality → Prime
-- Path Sum → Inequality -- → Induction
Path Lean → SetTheory → SetTheory2 → SetFunction → Module
Path Lean → Function → SetFunction
Path SetTheory2 → Numbers
Path Module → Basis → Module2
MakeGame

@ -0,0 +1,514 @@
import GameServer.Commands
-- Wird im Level "Implication 11" ohne Beweis angenommen.
LemmaDoc not_not as "not_not" in "Logic"
"
`not_not {A : Prop} : ¬¬A ↔ A`
## Eigenschaften
* `simp`-Lemma: Ja
* Namespace: `Classical`
* Minimal Import: `Std.Logic`
* Mathlib Doc: [#not_not](https://leanprover-community.github.io/mathlib4_docs/Std/Logic.html#Classical.not_not)
"
-- Wird im Level "Implication 10" ohne Beweis angenommen.
LemmaDoc not_or_of_imp as "not_or_of_imp" in "Logic"
"
`not_or_of_imp {A B : Prop} : (A → B) → ¬A B`
## Eigenschaften
* `simp`-Lemma: Nein
* Namespace: `-`
* Minimal Import: `Mathlib.Logic.Basic`
* Mathlib Doc: [#not_or_of_imp](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Logic/Basic.html#not_or_of_imp)
"
-- Wird im Level "Implication 12" bewiesen.
LemmaDoc imp_iff_not_or as "imp_iff_not_or" in "Logic"
"
`imp_iff_not_or {A B : Prop} : (A → B) ↔ (¬A B)`
## Eigenschaften
* `simp`-Lemma: Nein
* Namespace: `-`
* Minimal Import: `Mathlib.Logic.Basic`
* Mathlib Doc: [#imp_iff_not_or](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Logic/Basic.html#imp_iff_not_or)
"
LemmaDoc Nat.succ_pos as "succ_pos" in "Nat"
"
`Nat.succ_pos (n : ) : 0 < n.succ`
$n + 1$ ist strikt grösser als Null.
## Eigenschaften
* `simp` Lemma: Nein
* Namespace: `Nat`
* Minimal Import: `Mathlib.Init.Prelude`
* Mathlib Doc: [#Nat.succ_pos](https://leanprover-community.github.io/mathlib4_docs/Init/Prelude.html#Nat.succ_pos)
"
LemmaDoc Nat.pos_iff_ne_zero as "pos_iff_ne_zero" in "Nat"
"
`Nat.pos_iff_ne_zero {n : } : 0 < n ↔ n ≠ 0`
## Eigenschaften
* `simp`-Lemma: Nein
* Namespace: `Nat`
* Minimal Import: `Std.Data.Nat.Lemmas`
* Mathlib Doc: [#Nat.pos_iff_ne_zero](https://leanprover-community.github.io/mathlib4_docs/Std/Data/Nat/Lemmas.html#Nat.pos_iff_ne_zero)
"
-- TODO: Not minimal description
LemmaDoc zero_add as "zero_add" in "Addition"
"
`zero_add (a : ) : 0 + a = a`
## Eigenschaften
* `simp`-Lemma: Ja
* Namespace: `-`
* Import: `Mathlib.Nat.Basic`
* Mathlib Doc: [#zero_add](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/Group/Defs.html#zero_add)
"
LemmaDoc add_zero as "add_zero" in "Addition"
"
This lemma says `∀ a : , a + 0 = a`.
## Eigenschaften
* Mathlib Doc"
LemmaDoc add_succ as "add_succ" in "Addition"
"This lemma says `∀ a b : , a + succ b = succ (a + b)`.
## Eigenschaften
* Mathlib Doc: [#]()"
LemmaDoc not_forall as "not_forall" in "Logic"
"
`not_forall {α : Sort _} {P : α → Prop} : ¬(∀ x, → P x) ↔ ∃ x, ¬P x`
## Eigenschaften
* `simp`-Lemma: Ja
* Namespace: `-`
* Minimal Import: `Mathlib.Logic.Basic`
* Mathlib Doc: [#not_forall](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Logic/Basic.html#not_forall)
"
LemmaDoc not_exists as "not_exists" in "Logic"
"
`not_exists {α : Sort _} {P : α → Prop} : (¬∃ x, P x) ↔ ∀ (x : α), ¬P x.
## Eigenschaften
* `simp`-Lemma: Ja
* Namespace: `-`
* Minimal Import: `Std.Logic`
* Mathlib Doc: [#not_exists](https://leanprover-community.github.io/mathlib4_docs/Std/Logic.html#not_exists)"
LemmaDoc Nat.even_iff_not_odd as "even_iff_not_odd" in "Nat"
"
`even_iff_not_odd {n : } : Even n ↔ ¬Odd n`
## Eigenschaften
* `simp`-Lemma: Nein
* Namespace: `Nat`
* Minimal Import: `Mathlib.Data.Nat.Parity`
* Mathlib Doc: [#Nat.even_iff_not_odd](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Nat/Parity.html#Nat.even_iff_not_odd)"
LemmaDoc Nat.odd_iff_not_even as "odd_iff_not_even" in "Nat"
"
`Nat.odd_iff_not_even {n : } : Odd n ↔ ¬Even n`
## Eigenschaften
* `simp`-Lemma: Ja
* Namespace: `Nat`
* Minimal Import: `Mathlib.Data.Nat.Parity`
* Mathlib Doc: [#Nat.odd_iff_not_even](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Nat/Parity.html#Nat.odd_iff_not_even)"
LemmaDoc even_square as "even_square" in "Nat"
"
`even_square : (n : ), Even n → Even (n ^ 2)`
## Eigenschaften
* `simp`-Lemma: Nein
* *Nicht in Mathlib*
"
LemmaDoc Set.mem_univ as "mem_univ" in "Set"
"
`Set.mem_univ {α : Type _} (x : α) : x ∈ @univ α`
Jedes Element ist in `univ`, der Menge aller Elemente eines Typs `α`.
## Eigenschaften
* `simp`-Lemma: Ja
* Namespace: `Set`
* Minimal Import: `Mathlib.Data.Set.Basic`
* Mathlib Doc: [#mem_univ](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Set/Basic.html#Set.mem_univ)
"
LemmaDoc not_mem_empty as "not_mem_empty" in "Set"
"
`Set.not_mem_empty {α : Type _} (x : α) : x ∉ ∅`
Kein Element ist in der leeren Menge.
## Eigenschaften
* `simp`-Lemma: Nein
* Namespace: `Set`
* Minimal Import: `Mathlib.Data.Set.Basic`
* Mathlib Doc: [#not_mem_empty](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Set/Basic.html#Set.not_mem_empty)
"
LemmaDoc empty_subset as "empty_subset" in "Set"
"
`Set.empty_subset {α : Type u} (s : Set α) : ∅ ⊆ s`
## Eigenschaften
* `simp`-Lemma: Ja
* Namespace: `Set`
* Minimal Import: `Mathlib.Data.Set.Basic`
* Mathlib Doc: [#empty_subset](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Set/Basic.html#Set.empty_subset)
"
LemmaDoc Subset.antisymm as "Subset.antisymm" in "Set"
"
`Set.Subset.antisymm {α : Type u} {a : Set α} {b : Set α} (h₁ : a ⊆ b) (h₂ : b ⊆ a) : a = b`
Zwei Mengen sind identisch, wenn sowohl $A \\subseteq B$ wie auch $B \\subseteq A$.
## Details
`apply Subset.antisymm` ist eine Möglichkeit Gleichungen von Mengen zu zeigen.
eine andere ist `ext i`, welches Elementweise funktiniert.
Siehe auch
[`#Subset.antisymm_iff`](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Set/Basic.html#Set.Subset.antisymm_iff)
für die Iff-Version.
## Eigenschaften
* `simp`-Lemma: Nein
* Namespace: `Set.Subset`
* Minimal Import: `Mathlib.Data.Set.Basic`
* Mathlib Doc: [#Subset.antisymm](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Set/Basic.html#Set.Subset.antisymm)
"
LemmaDoc Subset.antisymm_iff as "Subset.antisymm_iff" in "Set"
"
`Set.Subset.antisymm_iff {α : Type u} {a : Set α} {b : Set α} : a = b ↔ a ⊆ b ∧ b ⊆ a`
Zwei Mengen sind identisch, wenn sowohl $A \\subseteq B$ wie auch $B \\subseteq A$.
## Details
`rw [Subset.antisymm_iff]` ist eine Möglichkeit Gleichungen von Mengen zu zeigen.
eine andere ist `ext i`, welches Elementweise funktiniert.
Siehe auch
[`#Subset.antisymm`](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Set/Basic.html#Set.Subset.antisymm)
für eine verwandte Version.
## Eigenschaften
* `simp`-Lemma: Nein
* Namespace: `Set.Subset`
* Minimal Import: `Mathlib.Data.Set.Basic`
* Mathlib Doc: [#Subset.antisymm_iff](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Set/Basic.html#Set.Subset.antisymm_iff)
"
LemmaDoc Nat.prime_def_lt'' as "prime_def_lt''" in "Nat"
"
`Nat.prime_def_lt'' {p : } :
Nat.Prime p ↔ 2 ≤ p ∧ ∀ (m : ), m p → m = 1 m = p`
Die bekannte Definition einer Primmzahl in ``: Eine Zahl (`p ≥ 2`) mit genau zwei Teilern.
## Eigenschaften
* `simp`-Lemma: Nein
* Namespace: `Nat`
* Mathlib Doc: [#Nat.prime_def_lt''](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Nat/Prime.html#Nat.prime_def_lt'')
"
LemmaDoc Finset.sum_add_distrib as "sum_add_distrib" in "Sum"
"
## Eigenschaften
* `simp`-Lemma: Nein
* Namespace: `Finset`
* Mathlib Doc: [#Finset.sum_add_distrib](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/BigOperators/Basic.html#Finset.sum_add_distrib)
"
LemmaDoc Fin.sum_univ_castSucc as "sum_univ_castSucc" in "Sum"
"
## Eigenschaften
* `simp`-Lemma: Nein
* Namespace: `Fin`
* Mathlib Doc: [#sum_univ_castSucc](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/BigOperators/Fin.html#Fin.sum_univ_castSucc)
"
LemmaDoc Nat.succ_eq_add_one as "succ_eq_add_one" in "Sum"
"
## Eigenschaften
* `simp`-Lemma: Nein
* Namespace: `Nat`
* Mathlib Doc: [#succ_eq_add_one](https://leanprover-community.github.io/mathlib4_docs/Init/Data/Nat/Basic.html#Nat.succ_eq_add_one)
"
LemmaDoc Nat.zero_eq as "zero_eq" in "Sum"
"
## Eigenschaften
* Mathlib Doc: [#zero_eq](https://leanprover-community.github.io/mathlib4_docs/Init/Data/Nat/Basic.html#Nat.zero_eq)
"
LemmaDoc add_comm as "add_comm" in "Nat"
"
## Eigenschaften
* Mathlib Doc: [#add_comm](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/Group/Defs.html#add_comm)
"
LemmaDoc mul_add as "mul_add" in "Nat"
"
## Eigenschaften
* Mathlib Doc: [#mul_add](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/Ring/Defs.html#mul_add)
"
LemmaDoc add_mul as "add_mul" in "Nat"
"
## Eigenschaften
* Mathlib Doc: [#add_mul](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/Ring/Defs.html#add_mul)
"
LemmaDoc arithmetic_sum as "arithmetic_sum" in "Sum"
"
## Eigenschaften
* Not a mathlib lemma.
"
LemmaDoc add_pow_two as "add_pow_two" in "Nat"
"
## Eigenschaften
* Mathlib Doc: [#add_pow_two](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/GroupPower/Ring.html#add_pow_two)
"
LemmaDoc Finset.sum_comm as "Finset.sum_comm" in "Sum"
"
## Eigenschaften
* Mathlib Doc: [#sum_comm](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/BigOperators/Basic.html#Finset.sum_comm)
"
LemmaDoc Function.comp_apply as "Function.comp_apply" in "Function"
"
## Eigenschaften
* Mathlib Doc: [#comp_apply](https://leanprover-community.github.io/mathlib4_docs/Init/Core.html#Function.comp_apply)
"
LemmaDoc not_le as "not_le" in "Logic"
"
## Eigenschaften
* Mathlib Doc: [#not_le](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Init/Algebra/Order.html#not_le)
"
LemmaDoc if_pos as "if_pos" in "Logic"
"
## Eigenschaften
* Mathlib Doc: [#if_pos](https://leanprover-community.github.io/mathlib4_docs/Init/Core.html#if_pos)
"
LemmaDoc if_neg as "if_neg" in "Logic"
"
## Eigenschaften
* Mathlib Doc: [#if_neg](https://leanprover-community.github.io/mathlib4_docs/Init/Core.html#if_neg)
"
LemmaDoc StrictMono.injective as "StrictMono.injective" in "Function"
"
## Eigenschaften
* Mathlib Doc: [#StrictMono.injective](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Order/Monotone/Basic.html#StrictMono.injective)
"
LemmaDoc StrictMono.add as "StrictMono.add" in "Function"
"
## Eigenschaften
* Mathlib Doc: [#StrictMono.add](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/Order/Monoid/Lemmas.html#StrictMono.add)
"
LemmaDoc Odd.strictMono_pow as "Odd.strictMono_pow" in "Function"
"
## Eigenschaften
* Mathlib Doc: [#Odd.strictMono_pow](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/Parity.html#Odd.strictMono_pow)
"
LemmaDoc Exists.choose as "Exists.choose" in "Function"
"
## Eigenschaften
* Mathlib Doc: [#Exists.choose](https://leanprover-community.github.io/mathlib4_docs/Std/Logic.html#Exists.choose)
"
LemmaDoc Exists.choose_spec as "Exists.choose_spec" in "Function"
"
## Eigenschaften
* Mathlib Doc: [#Exists.choose_spec](https://leanprover-community.github.io/mathlib4_docs/Std/Logic.html#Exists.choose_spec)
"
LemmaDoc congrArg as "congrArg" in "Function"
"
## Eigenschaften
* Mathlib Doc: [#congrArg](https://leanprover-community.github.io/mathlib4_docs/Init/Prelude.html#congrArg)
"
LemmaDoc congrFun as "congrFun" in "Function"
"
## Eigenschaften
* Mathlib Doc: [#congrFun](https://leanprover-community.github.io/mathlib4_docs/Init/Prelude.html#congrFun)
"
LemmaDoc Iff.symm as "Iff.symm" in "Logic"
"
## Eigenschaften
* Mathlib Doc: [#Iff.symm](https://leanprover-community.github.io/mathlib4_docs/Init/Core.html#Iff.symm)
"
/-! ## Definitions -/
DefinitionDoc Even as "Even"
"
`even n` ist definiert als `∃ r, a = 2 * r`.
Die Definition kann man mit `unfold even at *` einsetzen.
## Eigenschaften
* Mathlib Doc: [#Even](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/Parity.html#Even)"
DefinitionDoc Odd as "Odd"
"
`odd n` ist definiert als `∃ r, a = 2 * r + 1`.
Die Definition kann man mit `unfold odd at *` einsetzen.
* Mathlib Doc: [Odd](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/Parity.html#Odd)"
DefinitionDoc Injective as "Injective"
"
`Injective f` ist definiert als
```
∀ a b, f a = f b → a = b
```
definiert.
* Mathlib Doc: [Injective](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Init/Function.html#Function.Injective)"
DefinitionDoc Surjective as "Surjective"
"
`Surjective f` ist definiert als
```
∀ a, (∃ b, f a = b)
```
* Mathlib Doc: [Surjective](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Init/Function.html#Function.Surjective)"
DefinitionDoc Bijective as "Bijective"
"
## Eigenschaften
* Mathlib Doc: [#Bijective](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Init/Function.html#Function.Bijective)
"
DefinitionDoc LeftInverse as "LeftInverse"
"
## Eigenschaften
* Mathlib Doc: [#LeftInverse](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Init/Function.html#Function.LeftInverse)
"
DefinitionDoc RightInverse as "RightInverse"
"
## Eigenschaften
* Mathlib Doc: [#RightInverse](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Init/Logic.html#RightInverse)
"
DefinitionDoc StrictMono as "StrictMono"
"
`StrictMono f` ist definiert als
```
∀ a b, a < b → f a < f b
```
## Eigenschaften
* Mathlib Doc: [#StrictMono](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Order/Monotone/Basic.html#StrictMono)
"
DefinitionDoc Symbol.Subset as "⊆" "
Auf Mengen (`Set`) ist `A ⊆ B` als `∀x, x ∈ A → x ∈ B` implementiert.
"

@ -0,0 +1,16 @@
import Adam.Levels.Contradiction.L01_Have
import Adam.Levels.Contradiction.L02_Suffices
import Adam.Levels.Contradiction.L03_ByContra
import Adam.Levels.Contradiction.L04_ByContra
import Adam.Levels.Contradiction.L05_Contrapose
import Adam.Levels.Contradiction.L06_Summary
Game "Adam"
World "Contradiction"
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.
"

@ -1,13 +1,13 @@
import TestGame.Metadata import Adam.Metadata
import Std.Tactic.RCases import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight import Mathlib.Tactic.LeftRight
import Mathlib.Tactic.Contrapose import Mathlib.Tactic.Contrapose
import Mathlib.Tactic.Use import Mathlib.Tactic.Use
import Mathlib.Tactic.Ring import Mathlib.Tactic.Ring
import TestGame.ToBePorted import Adam.ToBePorted
Game "TestGame" Game "Adam"
World "Contradiction" World "Contradiction"
Level 1 Level 1

@ -1,13 +1,13 @@
import TestGame.Metadata import Adam.Metadata
import Std.Tactic.RCases import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight import Mathlib.Tactic.LeftRight
import Mathlib.Tactic.Contrapose import Mathlib.Tactic.Contrapose
import Mathlib.Tactic.Use import Mathlib.Tactic.Use
import Mathlib.Tactic.Ring import Mathlib.Tactic.Ring
import TestGame.ToBePorted import Adam.ToBePorted
Game "TestGame" Game "Adam"
World "Contradiction" World "Contradiction"
Level 2 Level 2

@ -1,4 +1,4 @@
import TestGame.Metadata import Adam.Metadata
import Std.Tactic.RCases import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight import Mathlib.Tactic.LeftRight
import Mathlib.Tactic.Contrapose import Mathlib.Tactic.Contrapose
@ -6,9 +6,9 @@ import Mathlib.Tactic.Use
import Mathlib.Tactic.Ring import Mathlib.Tactic.Ring
import Mathlib import Mathlib
import TestGame.ToBePorted import Adam.ToBePorted
Game "TestGame" Game "Adam"
World "Contradiction" World "Contradiction"
Level 3 Level 3

@ -1,4 +1,4 @@
import TestGame.Metadata import Adam.Metadata
import Std.Tactic.RCases import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight import Mathlib.Tactic.LeftRight
import Mathlib.Tactic.Contrapose import Mathlib.Tactic.Contrapose
@ -6,9 +6,9 @@ import Mathlib.Tactic.Use
import Mathlib.Tactic.Ring import Mathlib.Tactic.Ring
import Mathlib import Mathlib
import TestGame.ToBePorted import Adam.ToBePorted
Game "TestGame" Game "Adam"
World "Contradiction" World "Contradiction"
Level 4 Level 4

@ -1,12 +1,12 @@
import TestGame.Metadata import Adam.Metadata
import Std.Tactic.RCases import Std.Tactic.RCases
import Mathlib.Tactic.Contrapose import Mathlib.Tactic.Contrapose
import Mathlib.Tactic.Use import Mathlib.Tactic.Use
import Mathlib.Tactic.Ring import Mathlib.Tactic.Ring
import TestGame.ToBePorted import Adam.ToBePorted
Game "TestGame" Game "Adam"
World "Contradiction" World "Contradiction"
Level 5 Level 5

@ -1,12 +1,12 @@
import TestGame.Metadata import Adam.Metadata
import Std.Tactic.RCases import Std.Tactic.RCases
import Mathlib.Tactic.Contrapose import Mathlib.Tactic.Contrapose
import Mathlib.Tactic.Use import Mathlib.Tactic.Use
import Mathlib.Tactic.Ring import Mathlib.Tactic.Ring
import TestGame.ToBePorted import Adam.ToBePorted
Game "TestGame" Game "Adam"
World "Contradiction" World "Contradiction"
Level 6 Level 6

@ -0,0 +1,24 @@
import Adam.Levels.Function.L01_Function
import Adam.Levels.Function.L02_Let
import Adam.Levels.Function.L03_Piecewise
import Adam.Levels.Function.L04_Injective
import Adam.Levels.Function.L05_Injective
import Adam.Levels.Function.L06_Injective
import Adam.Levels.Function.L07_Surjective
import Adam.Levels.Function.L08_Bijective
import Adam.Levels.Function.L09_Inverse
import Adam.Levels.Function.L11_Inverse
Game "Adam"
World "Function"
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.
"

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Function" World "Function"
Level 1 Level 1

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Function" World "Function"
Level 2 Level 2

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Function" World "Function"
Level 3 Level 3

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Function" World "Function"
Level 4 Level 4

@ -1,9 +1,9 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
set_option tactic.hygienic false set_option tactic.hygienic false
Game "TestGame" Game "Adam"
World "Function" World "Function"
Level 5 Level 5

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Function" World "Function"
Level 6 Level 6

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Function" World "Function"
Level 7 Level 7

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Function" World "Function"
Level 8 Level 8
@ -33,6 +33,6 @@ Hint : Bijective (fun (n : ) ↦ n + 1) =>
Conclusion Conclusion
"Zufrieden drückt euch der Gelehrte eine neue Fackel in die Hand und "Zufrieden drückt euch der Gelehrte eine neue Fackel in die Hand und
zeigt euch den Weg nach draussen." zeigt euch den Weg nach draußen."
NewDefinition Bijective NewDefinition Bijective

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Function" World "Function"
Level 9 Level 9

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Function" World "Function"
Level 10 Level 10

@ -1,18 +1,18 @@
import TestGame.Levels.Implication.L01_Intro import Adam.Levels.Implication.L01_Intro
import TestGame.Levels.Implication.L02_Revert import Adam.Levels.Implication.L02_Revert
import TestGame.Levels.Implication.L03_Apply import Adam.Levels.Implication.L03_Apply
import TestGame.Levels.Implication.L04_Apply import Adam.Levels.Implication.L04_Apply
import TestGame.Levels.Implication.L05_Apply import Adam.Levels.Implication.L05_Apply
import TestGame.Levels.Implication.L06_Iff import Adam.Levels.Implication.L06_Iff
import TestGame.Levels.Implication.L07_Rw import Adam.Levels.Implication.L07_Rw
import TestGame.Levels.Implication.L08_Iff import Adam.Levels.Implication.L08_Iff
import TestGame.Levels.Implication.L09_Iff import Adam.Levels.Implication.L09_Iff
import TestGame.Levels.Implication.L10_Apply import Adam.Levels.Implication.L10_Apply
import TestGame.Levels.Implication.L11_ByCases import Adam.Levels.Implication.L11_ByCases
import TestGame.Levels.Implication.L12_Rw import Adam.Levels.Implication.L12_Rw
import TestGame.Levels.Implication.L13_Summary import Adam.Levels.Implication.L13_Summary
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Title "Aussagenlogik 2" Title "Aussagenlogik 2"
@ -28,7 +28,7 @@ aber niemand von den Einwohnern wusste was davon...
erzählen… erzählen…
Und damit leitet Robo den Landeanflug ein. Implis scheint ein riesiger Tagbau zu sein auf 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. dem nach allem möglichen gegraben wird. Überall seht ihr Förderbänder kreuz und quer.
Das Operationsteam begrüsst euch freundlich und lädt zum Essen im Kommandoturm. Das Operationsteam begrüsst euch freundlich und lädt zum Essen im Kommandoturm.
" "

@ -1,9 +1,9 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Tactic.Tauto import Mathlib.Tactic.Tauto
set_option tactic.hygienic false set_option tactic.hygienic false
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Level 1 Level 1
@ -18,7 +18,8 @@ Statement (A B : Prop) (hb : B) : A → (A ∧ B) := by
Hint "**Du**: Einen Moment, das ist eine Implikation (`\\to`), Hint "**Du**: Einen Moment, das ist eine Implikation (`\\to`),
also `A` impliziert `A und B`, soweit so gut, also eine Tautologie. also `A` impliziert `A und B`, soweit so gut, also eine Tautologie.
**Robo**: Die scheinen hier `tauto` auch nicht zu verstehen. **Robo**: Du hast recht, eigentlich könnte man `tauto` sagen,
aber das scheinen die hier tauto nicht zu verstehen.
Implikationen kannst du aber mit `intro h` angehen." 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, Hint "**Du**: Jetzt habe ich also angenommen, dass `A` wahr ist und muss `A ∧ B` zeigen,

@ -1,8 +1,8 @@
import TestGame.Metadata import Adam.Metadata
set_option tactic.hygienic false set_option tactic.hygienic false
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Level 2 Level 2

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Level 3 Level 3
@ -11,17 +11,10 @@ Introduction
" "
Sein Kollege zieht eine Linie unter deinen Beweis, schreibt ein durchgestrichenes ~`revert`~ Sein Kollege zieht eine Linie unter deinen Beweis, schreibt ein durchgestrichenes ~`revert`~
hin und gibt dir das Blatt wieder. hin und gibt dir das Blatt wieder.
`revert` ist aber nur selten der richtige Weg.
Im vorigen Beispiel würde man besser die Implikation $A \\Rightarrow B$ *anwenden*, also
sagen \"Es genügt $A$ zu zeigen, denn $A \\Rightarrow B$\" und danach $A$ beweisen.
Wenn man eine Implikation `(g : A → B)` in den Annahmen hat, bei welcher die Konsequenz
(also $B$) mit dem Goal übereinstimmt, kann man `apply g` genau dies machen.
" "
Statement (A B : Prop) (hA : A) (h : A → B) : B := by Statement (A B : Prop) (hA : A) (h : A → B) : B := by
Hint "**Robo**: Du hast natürlich recht, normalerweise ist es viel schöner mit Hint "**Robo**: Da hat er natürlich recht, normalerweise ist es viel schöner mit
`apply {h}` die Implikation anzuwenden." `apply {h}` die Implikation anzuwenden."
apply h apply h
Hint "**Du**: Und jetzt genügt es also `A` zu zeigen." Hint "**Du**: Und jetzt genügt es also `A` zu zeigen."

@ -1,8 +1,8 @@
import TestGame.Metadata import Adam.Metadata
set_option tactic.hygienic false set_option tactic.hygienic false
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Level 4 Level 4

@ -1,6 +1,6 @@
import TestGame.Metadata import Adam.Metadata
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Level 5 Level 5
@ -8,7 +8,7 @@ Title "Implikation"
Introduction Introduction
" "
Selbstsicher folgt ihr den Anweisungen und geht nach draussen zum Selbstsicher folgt ihr den Anweisungen und geht nach draußen zum
defekten Kontrollelement. Dieses zeigt ein kompliziertes Diagram: defekten Kontrollelement. Dieses zeigt ein kompliziertes Diagram:
$$ $$
\\begin{CD} \\begin{CD}

@ -1,6 +1,6 @@
import TestGame.Metadata import Adam.Metadata
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Level 6 Level 6
@ -37,7 +37,7 @@ hier bei `(h : A ↔ B)` heissen sie `h.mp` und `h.mpr`.
**Operationsleiter**: \"Modulo Ponens\" ist ein lokaler Begriff hier, **Operationsleiter**: \"Modulo Ponens\" ist ein lokaler Begriff hier,
aber das ist doch nicht wichtig. aber das ist doch nicht wichtig.
**Robo**: Und das \"r\" in `mpr` stünde für \"reverse\" weils die Rückrichtung ist. **Robo**: Und das \"r\" in `mpr` stünde für \"reverse\" weil's die Rückrichtung ist.
" "
NewTactic constructor NewTactic constructor

@ -1,9 +1,11 @@
import TestGame.Metadata import Adam.Metadata
import Init.Data.ToString import Init.Data.ToString
-- #check List UInt8 -- #check List UInt8
Game "TestGame" set_option tactic.hygienic false
Game "Adam"
World "Implication" World "Implication"
Level 7 Level 7

@ -1,8 +1,8 @@
import TestGame.Metadata import Adam.Metadata
set_option tactic.hygienic false set_option tactic.hygienic false
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Level 8 Level 8

@ -1,8 +1,8 @@
import TestGame.Metadata import Adam.Metadata
import Std.Tactic.RCases import Std.Tactic.RCases
import Mathlib.Tactic.Cases import Mathlib.Tactic.Cases
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Level 9 Level 9

@ -1,9 +1,9 @@
import TestGame.Metadata import Adam.Metadata
import Std.Tactic.RCases import Std.Tactic.RCases
import Mathlib.Tactic.Cases import Mathlib.Tactic.Cases
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Level 10 Level 10

@ -1,9 +1,9 @@
import TestGame.Metadata import Adam.Metadata
import Std.Tactic.RCases import Std.Tactic.RCases
import Mathlib.Tactic.Cases import Mathlib.Tactic.Cases
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Level 11 Level 11

@ -1,9 +1,9 @@
import TestGame.Metadata import Adam.Metadata
import Std.Tactic.RCases import Std.Tactic.RCases
import Mathlib.Tactic.Cases import Mathlib.Tactic.Cases
import Mathlib.Logic.Basic import Mathlib.Logic.Basic
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Level 12 Level 12

@ -1,11 +1,11 @@
import TestGame.Metadata import Adam.Metadata
import Std.Tactic.RCases import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight import Mathlib.Tactic.LeftRight
import Mathlib import Mathlib
set_option tactic.hygienic false set_option tactic.hygienic false
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Level 13 Level 13

@ -0,0 +1,5 @@
import Adam.Levels.Induction.L01_Induction
Game "Adam"
World "Induction"
Title "Übungen Induktions"

@ -1,10 +1,10 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
set_option tactic.hygienic false set_option tactic.hygienic false
Game "TestGame" Game "Adam"
World "Induction" World "Induction"
Level 1 Level 1

@ -0,0 +1,16 @@
import Adam.Levels.Inequality.L01_LE
import Adam.Levels.Inequality.L02_Pos
import Adam.Levels.Inequality.L03_Linarith
import Adam.Levels.Inequality.L04_Linarith
Game "Adam"
World "Inequality"
Title "Ungleichung"
Introduction "
Später erinnerst du dich gar nicht mehr wo und wann du diese Unterhaltung hattest, geschweige
denn mit wem. Vielleicht war es ein Traum, oder eine Erscheinung. Vielleicht war es
auch nur eines Abends über einer Runde Getränke.
Aber auf jedenfall hast du irgendwo gelernt, was du nun weisst.
"

@ -0,0 +1,34 @@
import Adam.Metadata
Game "Adam"
World "Inequality"
Level 1
Title "Kleinergleich"
Introduction
"
*(Gesrpäch)*
**Robo** (*lallend*, oder war's fröhlich proklamierend?):
…und deshalb sind `≥` und `>` eigentlich nur Notationen für `≤`,
welches man übrigens `\\le` schreibt, was für Less-Equal (also Kleinergleich) steht…
**Du**: Wir haben's verstanden, man benützt also Standartmässig lieber `≤` und `<`,
aber damit weiß ich eh nichts anzufangen.
**dritte Person**: Komm schon, das kannst du ja sicher:
"
Statement
(n m : ) : m < n ↔ m.succ ≤ n := by
Hint "**Robo**: Du Narr! Das ist doch eine Kuriosität, dass `m < n` auf `` per Definition
als `m + 1 ≤ n` definiert ist!
**dritte Person**: Du verdirbst den Witz! Ich wollte ihn doch nur testen."
rfl
OnlyTactic rfl
Conclusion "**Du**: Ha. ha… Na aber jetzt mal ehrlich, könnt ihr mir ein bisschen mehr
erzählen?"

@ -0,0 +1,53 @@
import Adam.Metadata
import Mathlib.Tactic.LibrarySearch
set_option tactic.hygienic false
Game "Adam"
World "Inequality"
Level 2
Title "Kleinergleich"
Introduction
"
*weitere Person*: …ich sag dir, eine positive Zahl kann man sowohl mit `0 < n`
als auch `n ≠ 0` darstellen.
*Robo*: Und da gibts leider keinen Standard dazu.
**weitere Person*: Ja und, da kann man ja einfach mit `Nat.pos_iff_ne_zero`
wechseln. Wart mal, wieso galt das nochmals…
"
Statement Nat.pos_iff_ne_zero (n : ) : 0 < n ↔ n ≠ 0 := by
Hint "**Robo** (*flüsternd*): Wenn du ein bisschen schwere Maschinerie auffahren willst,
um in zu beeindrucken, hab ich was. Mach doch eine Fallunterscheidung ob `n` Null ist
oder nicht!
**Du** (*flüsternd*): Und wie geht das?
**Robo** (*laut und selbstsicher*): Wir fangen mit `rcases n` an!"
rcases n
Hint "**Du**: Hmm, das muss man doch vereinfachen können.
**Robo** (*flüsternd*): Zweiter pompöser Auftritt: sag einfach `simp` und lass das alles
automatisch geschehen."
simp
Hint "**Du**: Und hier fang ich wohl am besten an wie ich das schon kenne."
constructor
intro
simp
intro
Hint "**Robo**: Warte! Den Rest geb ich dir als Lemma: `Nat.suc_pos`."
apply Nat.succ_pos
NewTactic simp
NewLemma Nat.succ_pos
DisabledLemma Nat.pos_iff_ne_zero
Conclusion "**Du**: Oh `simp` ist ja echt nicht schlecht…
Die andere Person scheint beeindruckt, hat aber gleichzeitig auch das Bedürfnis, Dich aus
der Reserve zu locken."

@ -0,0 +1,26 @@
import Adam.Metadata
import Mathlib.Tactic.Linarith
Game "Adam"
World "Inequality"
Level 3
Title "Linarith"
Introduction
"
**dritte Person**: Nah wenn wir so spielen:
"
Statement (n : ) (h : 2 ≤ n) : n ≠ 0 := by
Hint "**Du**: `simp` geht hier nicht, was mir ja auch einläuchtet.
**Robo**: Ist auch keine Vereinfachung, die du machen willst. Stattdessen,
`linarith` kann lineare Gleichungen und Ungleichungen lösen. Das ist das Powertool
in der hinsicht."
linarith
NewTactic linarith
NewLemma Nat.pos_iff_ne_zero
Conclusion "**Du**: Naja so beeindruckend war das jetzt auch noch nicht."

@ -0,0 +1,29 @@
import Adam.Metadata
import Mathlib.Tactic.Linarith
Game "Adam"
World "Inequality"
Level 4
Title "Linarith"
Introduction
"
**Robo**: Die Taktik kann aber noch viel mehr.
**weitere Person**: Hier, probier mal!
$$
\\begin{aligned}
5 * y &\\le 35 - 2 * x \\\\
2 * y &\\le x + 3
\\end{aligned}
$$
"
Statement (x y : ) (h₂ : 5 * y ≤ 35 - 2 * x) (h₃ : 2 * y ≤ x + 3) : y ≤ 5 := by
linarith
Conclusion "**Du**: Boah, das ist schon gar nicht schlecht.
Und damit endet auch Deine Erinnerung und wer weiss was du anschließend gemacht hast…"

@ -1,8 +1,8 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Inequality" World "Inequality"
Level 1 Level 1

@ -0,0 +1,26 @@
import Adam.Levels.Lean.L01_Type
import Adam.Levels.Lean.L02_Universe
import Adam.Levels.Lean.L03_ImplicitArguments
import Adam.Levels.Lean.L04_InstanceArguments
Game "Adam"
World "Lean"
Title "Lean"
Introduction
"Während ihr weiter durch Täler, über Geröllhalden und zwischen monumentalen Steintürmen
umherzieht, fragst Du eines Tages Robo.
**Du**: Sag mal, hast du dir je Gedanken dazu gemacht, wie du eigentlich funktionierts?
**Robo**: Was meinst du, wie ich funktioniere? Ich bin halt… ich…
**Du**: Ja schon, aber was woher weisst du denn alles was du weisst?
**Robo**: Das kann ich dir sagen. Früher habe ich viele Datenträger verschlungen,
und dadurch gelernt.
**Du**: Ob so eine Diskette wohl lecker schmeckt? Egal, ich hab ein paar Fragen zu deinem
Lean-Modul.
**Robo**: Na dann nur zu!"

@ -0,0 +1,41 @@
import Adam.Metadata
import Mathlib
set_option tactic.hygienic false
Game "Adam"
World "Lean"
Level 1
Title "Typen"
Introduction
"
**Du**: Also, wieso schreib ich denn sowohl `(n : )` für eine natürliche Zahl wie
auch `(h : A ∧ ¬ B)` für eine Aussage?
**Robo**: Alles in Lean sind Objekte von einem *Typen*, man nennt das auch
\"dependent type theory\". Rechts vom `:` steht immer der Typ der dieses Objekt hat.
**Du**: Verstehe, dann war `` der Typ der natürlichen Zahlen, `Prop` der Typ
aller logischen Aussagen, und so weiter. Un wenn `R` einfach irgendein Typ ist, dann…
**Robot: …würdest du das als `(R : Type)` schreiben.
**Du**: Also sind Typen ein bisschen jene Grundlage, die in meinem Studium die
Mengen eingenommen haben?
**Robo**: Genau. Ein Ring ist dann zum Beispiel als `(R : Type) [Ring R]` definiert,
also als Typen, auf dem eine Ringstruktur besteht.
**Robo**: Hier ein Beispiel. Die Taktik `ring` funktioniert in jedem Typen, der
genügend Struktur definiert hat, zum Beispiel in einem kommutativen Ring:
"
Statement (R : Type) [CommRing R] (a b : R) : a + b = b + a := by
ring
Conclusion "**Robo**: `[CommRing R]` nennt man übrigens eine Instanz und die
eckigen Klammern sagen Lean, dass es automatisch suchen soll, ob es so eine Instanz
findet, wenn man ein Lemma anwenden will."

@ -0,0 +1,44 @@
import Adam.Metadata
import Mathlib
set_option tactic.hygienic false
Game "Adam"
World "Lean"
Level 2
Title "Universen"
Introduction
"**Du**: Aber wenn alles Typen sind, welcher Typ hat dann `Type`?
**Robo**: `Type 1` und dieser hat Typ `Type 2`, etc.
**Robo**: Die Zahl nennt man *Universum*. Manchmal führt man Universen explizit
mit `universum u` ein, öfter siehst du `(R : Type _)`, was einfach ein Platzhalter
für irgend ein Universum ist.
**Du**: Das klingt ein bisschen nach Mengentheoretische Probleme, die man normalerweise
ignoriert.
**Robo**: Genau! Deshalb schreibt man eigentlich immer einfach `Type _` und ist glücklich.
Spezifischer muss man erst werden wenn man sowas wie Kategorientheorie anschaut, wo
man die Universen tatsächlich kontrollieren muss.
**Du**: Oke, hier rein, da raus. Aber hast du mir noch eine Aufgabe?
"
universe u
Statement
(R : Type u) [CommRing R] (a b : R) : a + b = b + a := by
Hint "**Robo**: Naja, Aufgaben zu Universen sind nicht so natürlich,
aber vorige Aufgabe würde man eigentlich besser so schreiben, da
kannst du mindestens das Uniersum beobachten."
ring
Conclusion "**Du**: Na dann. Aber gut dass ich's mal gesehen hab."
-- Hint (R : Type) (h : CommRing R) (a : R) (b : R) : a + b = b + a =>
-- ""

@ -0,0 +1,71 @@
import Adam.Metadata
import Mathlib
import Adam.ToBePorted
set_option tactic.hygienic false
Game "Adam"
World "Lean"
Level 3
Title "Implizite Argumente"
Introduction
"
**Du**: Was mich aber mehr beschäftigt, ist, dass Lemmas manchmal viel mehr Argumente
haben als ich hinschreiben muss.
**Robo**: Lean kann manche Argumente aus dem Kontext erschliessen. Hast du zum Beispiel
ein Lemma von vorhin
```
lemma Fin.sum_univ_castSucc {β : Type _} [AddCommMonoid β] {n : } (f : Fin (n + 1) → β) :
∑ i : Fin (n + 1), f i = ∑ i : Fin n, f (↑Fin.castSucc.toEmbedding i) + f (Fin.last n) := by
sorry
```
dann reicht es ja Lean `f` zu geben und daraus kann es herausfinden, was die anderen
(`β`, `n`) sein müssen.
**Robo**: Solche *implizite Argumente* markiert man dann mit `{_ : _}` während
*explizite Arumente* mit `(_ : _)` markiert werden.
**Du**: Dann könnte ich also einfach `Fin.sum_univ_castSucc f` schreiben?
**Robo**: Genau!
**Du**: Und was war dann das `(n := m + 1)` vorhin genau?
**Robo**: Damit kann man im Aussnahmefall die impliziten Argumente doch angeben. Hier haben wir
gesagt, es soll für das Argument `n` den Term `m + 1` einsetzen. Hier mach das doch noch einmal
unter weniger Stress:
"
open BigOperators
Statement (m : ) : ∑ i : Fin (m + 1), (i : ) + (m + 1) = ∑ i : Fin (Nat.succ m + 1), ↑i := by
Branch
rw [Fin.sum_univ_castSucc]
Hint "**Robo**: Siehst du, ohne die Hilfe macht es das Falsche. Deshalb muss man hier
explizit mit `Fin.sum_univ_castSucc (n := m + 1)` nachhelfen."
rw [Fin.sum_univ_castSucc]
Hint "**Robo**: Na klar, in dem Beispiel kannst du einfach weiter umschreiben bis es
nicht mehr geht, aber das war nicht der Punkt…"
rw [Fin.sum_univ_castSucc]
Hint "**Robo**: Na klar, in dem Beispiel kannst du einfach weiter umschreiben bis es
nicht mehr geht, aber das war nicht der Punkt…"
rfl
rw [Fin.sum_univ_castSucc (n := m + 1)]
rfl
OnlyTactic rw rfl
Conclusion "**Du**: Gibt es auch noch ander Methoden implizite Argumente anzugeben.
**Robo** `@Fin.sum_univ_castSucc` würde *alle* Argumente explizit machen,
aber das ist unparktischer, weil man dann irgendwie
`@Fin.sum_univ_castSucc _ _ (m + 1)` schreiben müsste.
**Du**: Ah und ich sehe der `_` ist überall in Lean ein Platzhalter, der automatisch
gefüllt wird."

@ -0,0 +1,57 @@
import Adam.Metadata
import Mathlib
import Adam.ToBePorted
set_option tactic.hygienic false
Game "Adam"
World "Lean"
Level 4
Title "Instanz-Argumente"
Introduction
"**Du**: Also nochmals als Zusammenfassung, dann gibt es 3 Arten von Argumenten,
explizite mit `()`, implizite mit `{}` und Instanzen mit `[]`?
**Robo**: Korrekt. Instanzen sind damit auch Implizite Argumente. Der Unterschied
zwischen `{}` und `[]` ist also *wie* Lean diese füllt.
**Du**: Verstehe, bei den ersten sucht es logisch nach einer richtigen Möglichkeit,
beim zweiten geht's durch alle Instanzen, die es kennt.
**Robo**: Funktioniert hier bei mir nicht, aber wenn du ausserhalb eines Beweises
`#synth Ring ` in ein Dokument schreibt, zeigt dir Lean, ob es eine Instanz finden kann.
**Du**: Ich glaube das macht alles Sinn.
**Robo**: Hier, mach nochmals das Gleiche wie vorhin aber mit @-Syntax um das zu
verinnerlichen:
"
open BigOperators
Statement (m : ) :
∑ i : Fin (m + 1), (i : ) + (m + 1) = ∑ i : Fin (Nat.succ m + 1), ↑i := by
Hint "*Robo*: Schreibe `rw [@Fin.sum_univ_castSucc _ _ (m + 1)]`
anstatt `rw [Fin.sum_univ_castSucc (n := m + 1)]`!"
rw [@Fin.sum_univ_castSucc _ _ (m + 1)]
rfl
OnlyTactic rw rfl
Conclusion "
**Du**: Danke Robo!
Um zwei weitere Ecken und plötzlich steht ihr wieder vor dem Golem, dem ihr schon begegnet seit.
Dieser lädt euch zum Abendmahl ein. Ihr erfährt, dass er ganz gerne liest und er zeigt euch
sein neustes Buch, das er leider nicht lesen kann. Nich tnur ist es der zweite Band einer Serie
(der Erste hat offensichtlich was mit \"Urbildern\" zu tun), sondern ist es auch in einem
Dialekt geschrieben, der anscheinend nur auf einem Nachbarsmond gesprochen wird.
Ihr beschliesst dem herzlichen Golem zu helfen und beiden Monden einen Besuch abzustatten,
sowohl um den Dialekt zu lernen, wie auch in der Bibliothek auf dem anderen Mond nach dem
ersten Band zu suchen.
"

@ -1,10 +1,10 @@
import TestGame.Metadata import Adam.Metadata
import Std.Tactic.RCases import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight import Mathlib.Tactic.LeftRight
set_option tactic.hygienic false set_option tactic.hygienic false
Game "TestGame" Game "Adam"
World "Implication" World "Implication"
Level 9 Level 9

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Tactic.Ring import Mathlib.Tactic.Ring
Game "TestGame" Game "Adam"
World "Nat2" World "Nat2"
Level 3 Level 3

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Tactic.Ring import Mathlib.Tactic.Ring
Game "TestGame" Game "Adam"
World "Nat2" World "Nat2"
Level 5 Level 5

@ -1,4 +1,4 @@
import TestGame.Metadata import Adam.Metadata
import Std.Tactic.RCases import Std.Tactic.RCases
import Mathlib.Tactic.Contrapose import Mathlib.Tactic.Contrapose
import Mathlib.Tactic.Use import Mathlib.Tactic.Use
@ -21,7 +21,7 @@ lemma even_square (n : ) : even n → even (n ^ 2) := by
def prime (n : ) : Prop := (2 ≤ n) ∧ ∀ a b, n = a * b → a = 1 b = 1 def prime (n : ) : Prop := (2 ≤ n) ∧ ∀ a b, n = a * b → a = 1 b = 1
Game "TestGame" Game "Adam"
World "Nat" World "Nat"
Level 4 Level 4

@ -1,4 +1,4 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
-- -- INCORPORATED -- -- INCORPORATED

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Tactic.Ring import Mathlib.Tactic.Ring
Game "TestGame" Game "Adam"
World "Function" World "Function"
Level 1 Level 1

@ -0,0 +1,41 @@
import Adam.Levels.LinearAlgebra.L01_Module
import Adam.Levels.LinearAlgebra.L02_VectorNotation
import Adam.Levels.LinearAlgebra.L03_VectorNotation
import Adam.Levels.LinearAlgebra.L04_Submodule
import Adam.Levels.LinearAlgebra.L05_Submodule
import Adam.Levels.LinearAlgebra.L06_Span
import Adam.Levels.LinearAlgebra.L07_Span
import Adam.Levels.LinearAlgebra.L08_GeneratingSet
import Adam.Levels.LinearAlgebra.M01_LinearMap
import Adam.Levels.LinearAlgebra.M02_LinearIndep
import Adam.Levels.LinearAlgebra.M04_Basis
import Adam.Levels.LinearAlgebra.N01_Span
import Adam.Levels.LinearAlgebra.N02_Span
import Adam.Levels.LinearAlgebra.N03_Idempotent
import Adam.Levels.LinearAlgebra.N04_Idempotent
import Adam.Levels.LinearAlgebra.N05_Sum
import Adam.Levels.LinearAlgebra.N06_Sum
import Adam.Levels.LinearAlgebra.N07_Prod
import Adam.Levels.LinearAlgebra.N08_Prod
import Adam.Levels.LinearAlgebra.N09_Prod
Game "Adam"
World "Module"
Title "Vektorraum"
Introduction "Hier lernst du die Grundlagen zur linearen Algebra.
Vektorräume sind in Lean etwas algemeiner definiert als dies normalerweise in
einer Einführungsvorlesung antrifft: Man definiert ein \"Modul\" (Plural: Moduln)
über einem Ring. Ein Modul über einem *Körper* wird dann auch \"Vektorraum\" genannt.
"
Game "Adam"
World "Basis"
Title "Lineare Abbildungen"
Game "Adam"
World "Module2"
Title "Mehr Vektorräume"

@ -0,0 +1,106 @@
import Adam.Metadata
import Mathlib.Data.Real.Basic -- definiert ``
import Mathlib.Algebra.Module.Basic -- definiert `module`
import Mathlib.Tactic.LibrarySearch
import Adam.StructInstWithHoles
set_option tactic.hygienic false
Game "Adam"
World "Module"
Level 1
Title "Module"
Introduction
"
Konkret heisst das:
Sei `R` ein Ring. Ein `R`-Modul ist eine kommutative Gruppe `(V, +)` zusammen mit
einer Abbildung `• : R × V → V` (Skalarmultiplitation genannt), die folgende
Eigenschaften beliebige `(r s : R)` und `(v w : V)`erfüllt:
- `r • (v + w) = r • v + r • w`
- `(r + s) • v = r • v + s • v`
- `(r * s) • v = r • s • v`
- `1 • r = r`
- `0 • v = 0`
- `r • 0 = 0`
"
-- Bemerkungen:
-- 1) Über einem `[field R]` sind die letzten beiden Eigenschaften überflüssig, diese kann
-- man beweisen, wenn man Cancellation (`a₁ + x = a₂ + x → a₁ = a₂`) hat. In einem generellen
-- Ring, muss das aber nicht gegeben sein (siehe Nullteiler).
-- 2) Die nötigen Annahmen um ein Modul in Lean zu erhalten sind tatsächlich noch etwas lockerer,
-- so muss `R` nicht ganz ein Ring sein (nur `[Semiring R]`) und
-- `V` muss nicht ganz eine kommutative Gruppe sein (nur `[add_comm_monoid V]`).
-- Einen abstrakten Vektorraum definiert man wie folgt:
-- `variables {R V : Type*} [field R] [add_comm_monoid V] [module R V]`
-- Wenn man hingegen konkret zeigen will, dass ein existierendes Objekt ein Vektorraum ist,
-- kann man eine einsprechende Instanz wie folgt definieren:
-- ```
-- instance Q_module : Module :=
-- { smul := λa r, ↑a * r
-- smul_zero := _
-- zero_smul := _
-- one_smul := _
-- smul_add := _
-- add_smul := _
-- mul_smul := _ }
-- ```
-- Man muss also angeben, welche Skalarmultiplikation man gerne hätte
-- (`smul`. In unserem Fall sagen wir einfach, diese soll Multiplikation in `` sein.)
-- und dazu jegliche Beweise, dass die Skalarmultiplikation sich mit der Ringstruktur verträgt.
-- Im nachfolgenden beweisen wir die Eigenschaften einzeln.
Statement
"Zeige, dass $\\mathbb{R}$ ein $\\mathbb{Q}$-Modul ist."
: Module := by
Hint "**Robo**: Als erstes willst du die Stuktur `Modul` aufteilen in einzelne Beweise.
Der Syntax dafür ist:
```
refine \{ ?..! }
```
"
refine { ?..! }
· Hint "**Robo**: Hier musst du die Skalarmultiplikation angeben.
Benutze dafür `exact fun (a : ) (r : ) => ↑a * r`."
exact fun (a : ) (r : ) => ↑a * r
· Hint "**Robo**: Jetzt musst du alle eigenschaften eines $\\mathbb\{Q}$-Moduls zeigen,
das sind also 6 einzelne Goals."
intro b
Hint "**Robo**: Mit `change (1 : ) * {b} = {b}` kannst du das Goal umschreiben, damit
dann andere Taktiken besser damit arbeiten können."
change (1 : ) * b = b
Hint "**Robo**: Den Teil kannst du mit `simp` beweisen."
simp
· intro x y b
Hint "**Robo**: Benutze `change` um `•` durch `*` zu ersetzen."
Hint (hidden := true) "**Robo**: Explizit `change ({x} * {y} : ) * {b} = {x} * ({y} * {b})`"
change (x * y : ) * b = x * (y * b)
Hint "**Robo**: Mit `simp` und `ring` kannst du den Rest beweisen."
simp
ring
· intro a
simp
· intro a x y
Hint (hidden := true) "**Robo**: Explizit `change {a} * ({x} + {y}) = {a} * {x} + {a} * {y}`"
change a * (x + y) = a * x + a * y
ring
· intro r s x
Hint (hidden := true) "**Robo**: Explizit
`change ({r} + {s} : ) * {x} = {r} * {x} + {s} * {x}`"
change (r + s : ) * x = r * x + s * x
simp
ring
· intro a
Hint (hidden := true) "**Robo**: Explizit `change (0 : ) * {a} = 0`"
change (0 : ) * a = 0
simp
NewTactic refine exact change

@ -1,4 +1,4 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Data.Real.Basic -- definiert `` import Mathlib.Data.Real.Basic -- definiert ``
import Mathlib.Algebra.Module.Pi -- definiert `Module (fin 2 → )` import Mathlib.Algebra.Module.Pi -- definiert `Module (fin 2 → )`
@ -7,7 +7,7 @@ import Mathlib.Tactic.FinCases
set_option tactic.hygienic false set_option tactic.hygienic false
Game "TestGame" Game "Adam"
World "Module" World "Module"
Level 2 Level 2
@ -15,30 +15,14 @@ Title "Konkrete Vektorräume"
Introduction Introduction
" "
Häufig in der linearen Algebra hat man ganz einfach Vektoren über `` oder `` Den $\\mathbb{Q}$-Vektorraum $\\mathbb{Q}^3$ definiert man am besten mit
und möchte diese explizit definieren. der lokalen Notation
Reele Vektorräume definiert man in Lean am besten als Funktion von einem Indexset.
So ist `Fin 2` eine Menge, die nur `0` und `1` enthält
und `ℚ²` wird als `Fin 2 → ` definiert. Hier machen wir uns eine
lokale notation dafür:
``` ```
notation `ℚ²` := Fin 2 local notation `ℚ³` := Fin 3 →
``` ```
Das mag auf den ersten Blick etwas unintuitiv erscheinen, hat aber den Vorteil, Dabei ist `Fin 3` die Indexmenge $\\{0, 1, 2\\}$.
dass man die ganze
Infrastruktur für Funktionen einfach direkt für Vektoren und
Matrizen mitgebrauchen kann.
Wir können uns auch noch ein paar andere standardmässige ``-Vektorräume definieren.
```
notation \"ℚ³\" => Fin 3 →
notation \"^(\" n \")\" => (Fin n) →
```
Die schreibweise für einen Vektor ist `![ _, _ ]`. Zu beachten ist, Die schreibweise für einen Vektor ist `![ _, _ ]`. Zu beachten ist,
dass man bei Zahlen explizit einen Typ angeben muss, sonst denkt sich dass man bei Zahlen explizit einen Typ angeben muss, sonst denkt sich
@ -61,8 +45,8 @@ Dann eine Vektorgleichung komponentenweise aufteilt.
Für jede Komponente kann man dann mit `simp` und `ring` weiterkommen. Für jede Komponente kann man dann mit `simp` und `ring` weiterkommen.
" "
notation "ℚ³" => Fin 3 → local notation "ℚ³" => Fin 3 →
notation "^(" n ")" => (Fin n) → local notation "^(" n ")" => (Fin n) →
Statement Statement
"" ""

@ -1,11 +1,11 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Data.Real.Basic -- definiert `` import Mathlib.Data.Real.Basic -- definiert ``
import Mathlib.Algebra.Module.Pi -- definiert `Module (fin 2 → )` import Mathlib.Algebra.Module.Pi -- definiert `Module (fin 2 → )`
import Mathlib.Data.Fin.VecNotation import Mathlib.Data.Fin.VecNotation
import Mathlib.Tactic.FinCases import Mathlib.Tactic.FinCases
Game "TestGame" Game "Adam"
World "Module" World "Module"
Level 3 Level 3

@ -1,8 +1,8 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
Game "TestGame" Game "Adam"
World "Module" World "Module"
Level 4 Level 4

@ -1,8 +1,8 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
Game "TestGame" Game "Adam"
World "Module" World "Module"
Level 5 Level 5

@ -1,4 +1,4 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib.Data.Real.Basic -- definiert `` import Mathlib.Data.Real.Basic -- definiert ``
@ -8,7 +8,7 @@ import Mathlib.Tactic.FinCases
import Mathlib.Algebra.BigOperators.Finsupp -- default? import Mathlib.Algebra.BigOperators.Finsupp -- default?
import Mathlib.LinearAlgebra.Span import Mathlib.LinearAlgebra.Span
Game "TestGame" Game "Adam"
World "Module" World "Module"
Level 6 Level 6

@ -1,4 +1,4 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib.Data.Real.Basic -- definiert `` import Mathlib.Data.Real.Basic -- definiert ``
@ -10,7 +10,7 @@ import Mathlib.LinearAlgebra.Span
import Mathlib.Tactic.LibrarySearch import Mathlib.Tactic.LibrarySearch
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Module" World "Module"
Level 7 Level 7

@ -1,4 +1,4 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib.Data.Real.Basic -- definiert `` import Mathlib.Data.Real.Basic -- definiert ``
@ -8,7 +8,7 @@ import Mathlib.Tactic.FinCases
import Mathlib.Algebra.BigOperators.Finsupp -- default? import Mathlib.Algebra.BigOperators.Finsupp -- default?
import Mathlib.LinearAlgebra.Span import Mathlib.LinearAlgebra.Span
Game "TestGame" Game "Adam"
World "Module" World "Module"
Level 8 Level 8

@ -1,11 +1,11 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Data.Real.Basic -- definiert `` import Mathlib.Data.Real.Basic -- definiert ``
import Mathlib.Algebra.Module.LinearMap -- definiert `→ₗ` import Mathlib.Algebra.Module.LinearMap -- definiert `→ₗ`
import Mathlib.Tactic.FinCases import Mathlib.Tactic.FinCases
import Mathlib.Data.Fin.VecNotation import Mathlib.Data.Fin.VecNotation
Game "TestGame" Game "Adam"
World "Basis" World "Basis"
Level 1 Level 1

@ -0,0 +1,49 @@
import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib.Data.Real.Basic -- definiert ``
import Mathlib.Algebra.Module.LinearMap -- definiert `→ₗ`
import Mathlib.Tactic.FinCases
import Mathlib.Data.Fin.VecNotation
-- import Mathlib.LinearAlgebra.Finsupp
import Mathlib.Algebra.BigOperators.Basic -- default
-- import Mathlib.LinearAlgebra.LinearIndependent
import Mathlib
Game "Adam"
World "Basis"
Level 2
Title "Lineare Unabhängigkeit"
namespace Ex_LinIndep
scoped notation "ℝ²" => Fin 2 →
Introduction
"
"
Statement
"Zeige, dass `![1, 0], ![1, 1]` linear unabhängig über `` sind."
: LinearIndependent ![(![1, 0] : ℝ²), ![1, 1]] := by
Hint "`rw [Fintype.linearIndependent_iff]`"
rw [Fintype.linearIndependent_iff]
Hint "`intros c h`"
intros c h
Hint "BUG: `simp at h` does not work :("
simp at h -- doesn't work
sorry
-- rw [Fintype.linearIndependent_iff]
-- intros c h
-- simp at h
-- intros i
-- fin_cases i
-- swap
-- { exact h.2 }
-- { have h' := h.1
-- rw [h.2, add_zero] at h'
-- exact h'}
end Ex_LinIndep

@ -0,0 +1,32 @@
import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib
Game "Adam"
World "Basis"
Level 4
Title "Basis"
namespace Ex_Basis
scoped notation "ℝ²" => Fin 2 →
open Submodule
Introduction
"
"
lemma exercise1 : LinearIndependent ![(![1, 0] : ℝ²), ![1, 1]] := sorry
lemma exercise2 : ≤ span (Set.range ![(![1, 0] : Fin 2 → ), ![1, 1]]) := sorry
Statement : Basis (Fin 2) ℝ² := by
apply Basis.mk
apply exercise1
apply exercise2
end Ex_Basis

@ -1,4 +1,4 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib.Data.Real.Basic -- definiert `` import Mathlib.Data.Real.Basic -- definiert ``
@ -8,7 +8,7 @@ import Mathlib.Tactic.FinCases
import Mathlib.Algebra.BigOperators.Finsupp -- default? import Mathlib.Algebra.BigOperators.Finsupp -- default?
import Mathlib.LinearAlgebra.Span import Mathlib.LinearAlgebra.Span
Game "TestGame" Game "Adam"
World "Module2" World "Module2"
Level 1 Level 1

@ -1,4 +1,4 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib.Data.Real.Basic -- definiert `` import Mathlib.Data.Real.Basic -- definiert ``
@ -11,7 +11,7 @@ import Mathlib
open Submodule open Submodule
Game "TestGame" Game "Adam"
World "Module2" World "Module2"
Level 2 Level 2

@ -1,10 +1,10 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib.Data.Real.Basic import Mathlib.Data.Real.Basic
import Mathlib.LinearAlgebra.Basic import Mathlib.LinearAlgebra.Basic
Game "TestGame" Game "Adam"
World "Module2" World "Module2"
Level 3 Level 3

@ -1,10 +1,10 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib.Data.Real.Basic import Mathlib.Data.Real.Basic
import Mathlib.LinearAlgebra.Basic import Mathlib.LinearAlgebra.Basic
Game "TestGame" Game "Adam"
World "Module2" World "Module2"
Level 4 Level 4

@ -1,11 +1,11 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib.LinearAlgebra.Span import Mathlib.LinearAlgebra.Span
open Submodule open Submodule
Game "TestGame" Game "Adam"
World "Module2" World "Module2"
Level 5 Level 5

@ -1,11 +1,11 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib.LinearAlgebra.Span import Mathlib.LinearAlgebra.Span
open Submodule open Submodule
Game "TestGame" Game "Adam"
World "Module2" World "Module2"
Level 6 Level 6

@ -1,10 +1,10 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib.Data.Real.Basic import Mathlib.Data.Real.Basic
import Mathlib.LinearAlgebra.Span import Mathlib.LinearAlgebra.Span
Game "TestGame" Game "Adam"
World "Module2" World "Module2"
Level 7 Level 7

@ -1,4 +1,4 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib.Data.Real.Basic import Mathlib.Data.Real.Basic
@ -6,7 +6,7 @@ import Mathlib.LinearAlgebra.Span
universe u universe u
Game "TestGame" Game "Adam"
World "Module2" World "Module2"
Level 8 Level 8

@ -1,10 +1,10 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Algebra.Module.Submodule.Lattice
import Mathlib.Data.Real.Basic import Mathlib.Data.Real.Basic
import Mathlib.LinearAlgebra.Span import Mathlib.LinearAlgebra.Span
Game "TestGame" Game "Adam"
World "Module2" World "Module2"
Level 9 Level 9

@ -0,0 +1,2 @@
import Adam.Levels.Numbers.L01_PNat
import Adam.Levels.Numbers.L02_PNat

@ -1,7 +1,7 @@
import TestGame.Metadata import Adam.Metadata
import Mathlib import Mathlib
Game "TestGame" Game "Adam"
World "Numbers" World "Numbers"
Level 1 Level 1

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

Loading…
Cancel
Save