merge necessary as upstream developed while storyline slept

This commit is contained in:
Marcus Zibrowius
2023-03-29 11:25:46 +02:00
899 changed files with 17509 additions and 2704 deletions
@@ -0,0 +1,46 @@
import Adam.Metadata
import Mathlib.Tactic.Tauto
Game "Adam"
World "Proposition"
Level 1
Title "Automatisierung"
Introduction
"
Gerade seid Ihr auf Königin *Logisindes* Planeten. Sie kommt ohne Umschweife zum Punkt:
**Logisinde** Werte Wesen aus fremden Welten, gestatten Sie eine Frage. Warum gilt …
Und sie kritzelt etwas auf ein Stück Papier: oben ein paar Annahmen, unten eine Schlussfolgerung.
Dazwischen sollst Du offenbar einen Beweis eintragen.
Du siehst Robo hilflos an.
"
Statement ""
(A B C : Prop) :
¬((¬B ∨ ¬ C) ∨ (A → B)) → (¬A ∨ B) ∧ ¬ (B ∧ C) := by
Hint "**Robo** Das ist ganz einfach. Mit `{A} {B} {C} : Prop` meint sie:
`{A}`, `{B}` und `{C}` sind irgendwelche Aussagen (*propositions*).
Und mit `→` meint sie ⇒, also “impliziert”. Die anderen Symbole kennst Du, oder?
**Du** Ehhm, ja. Aber da muss ich jetzt trotzdem erst einmal überlegen.
**Robo** (flüsternd) Behaupte doch einfach, dass sei eine Tautologie.
**Du** Ernsthaft?
**Robo** Ja. Schreib einfach `tauto`.
**Robo** Mach schon …"
tauto
Conclusion
"
**Logisinde** (etwas konsterniert) Ja, das ist streng genommen richtig.
Aber glaubt bloß nicht, dass Ihr damit auf *diesem* Planeten viel weiterkommt!
Meine Untertanen verstehen `tauto` nicht. Da müsst Ihr Euch schon etwas mehr anstrengen.
"
NewTactic tauto
@@ -0,0 +1,32 @@
import Adam.Metadata
Game "Adam"
World "Proposition"
Level 2
Title "Aller Anfang ist... ein Einzeiler?"
Introduction
"
In der Zwischenzeit hat bereits sich eine lange Schlange Untertanen gebildet, die gern ihren
Fragen stellen würden. Logisinde winkt den ersten nach vorn. Er räuspert sich.
**Untertan** Warum ist $42 = 42$?
Du schaust ihn fassungslos an.
Er schreibt es Dir wieder auf.
"
Statement "" :
42 = 42 := by
Hint " **Robo** Ist doch klar. Du musst ihn einfach daran erinnern,
dass Gleichheit *reflexiv* ist. Probier mal `rfl`."
rfl
Conclusion
"
**Untertan** Ah, richtig. Ja, Sie haben ja so recht. Das vergesse ich immer. Rfl, rfl, rfl …
"
NewTactic rfl
DisabledTactic tauto
@@ -0,0 +1,43 @@
import Adam.Metadata
Game "Adam"
World "Proposition"
Level 3
Title "Annahmen"
Introduction
"
Während der erste Untertan noch rfl, rfl, rfl murmelt, tritt schon der nächste nach vorne. Es ist schüchtern und schreibt bloß.
"
Statement ""
(n : ℕ) (h₁ : 10 > n) (h₂ : 1 < n) (h₃ : n ≠ 5) : 1 < n := by
Hint "
**Robo** `{n} : ℕ` bedeutet, `{n}` ist eine natürliche Zahl.
**Du** Warum schreibt er dann nicht `{n} ∈ ℕ`??
**Robo** Weil das hier alles komische Typen sind … Ich kann Dir das später mal in Ruhe erklären.
Jetzt will ich erst einmal die Frage entschlüsseln.
**Robo** Also, `{h₁}`, `{h₂}`, `{h₃}` sind einfach nur Namen für verschiedene Annahmen, und zwar
für die Annahme `n < 10`, `1 < n` und `n ≠ 5`. Beweisen sollen wir: `1 < n`.
**Du** Aber das war doch gerade eine der Annahmen.
**Robo** Ja, stimmt.
**Du** ???
**Robo** Du musst ihm das halt explizit sagen. Probiers mal mit `assumption`."
assumption
Conclusion
"
**Untertan** Ja richtig! Wenn Ihr nur wüsstet, was ich mir an dieser Frage schon den Kopf
zerbrochen habe!
"
NewTactic assumption
DisabledTactic tauto
@@ -0,0 +1,36 @@
import Adam.Metadata
import Mathlib.Data.Nat.Basic -- TODO
Game "Adam"
World "Proposition"
Level 4
Title "Logische Aussagen"
Introduction
"
Ein dritter Untertan kommt mit folgendem Problem.
"
Statement ""
(A : Prop) (hA : A) : A := by
Hint "
**Robo** Hier bedeutet `{A} : Prop` wieder, dass `{A}` irgendeine Aussage ist.
Und `{hA}` ist eine Name für die Annahme, dass `{A}` wahr ist.
**Du** Und unter dieser Annahme sollen wir jetzt `{A}` beweisen?
**Robo** Ja. Da kommst Du jetzt selbst drauf, wie das geht, oder?
"
Hint (hidden := true) "**Robo**: Ist doch genau wie eben:
die Aussage, die zu beweisen ist, gehört selbst zu den Annahmen.
Also wird `assumption` auch wieder funktionieren."
assumption
Conclusion
"
**Untertan** Das ging ja schnell. Super! Vielen Dank.
"
NewTactic assumption
DisabledTactic tauto
@@ -0,0 +1,37 @@
import Adam.Metadata
Game "Adam"
World "Proposition"
Level 5
Title "True/False"
Introduction
"
Der nächste Untertan in der Reihe ist ein Schelm.
"
Statement "" : True := by
Hint "
**Robo** Dieses `True` ist eine spezielle Aussage, nämlich die Aussage, die immer und
bedingungslos wahr ist.
**Du** Und was genau ist dann zu beweisen?
**Robo** Ich glaube, nichts. Ich glaube, Du kannst einfach `trivial` schreiben.
"
trivial
Conclusion
"
**Schelm** Wollte nur mal sehen, dass Ihr nicht auf den Kopf gefallen seid …
**Du** *(zu Robo)* Können wir nicht einfach immer dieses `trivial` verwenden?
Wie in einer Mathe-Vorlesung?
**Robo** Nein, das `trivial` hier hat eine ziemlich spezielle Bedeutung.
Das funktioniert nur in einer Handvoll Situationen.
"
NewTactic trivial
DisabledTactic tauto
@@ -0,0 +1,34 @@
import Adam.Metadata
Game "Adam"
World "Proposition"
Level 6
Title "Not"
Introduction
"
Der Schelm hat noch eine Schwester dabei.
"
Statement "" : ¬False := by
Hint "
**Robo** Dieses Zeichen `¬` bedeutet Negation. Also wenn eine Aussage `(A : Prop)`
wahr ist, dann ist `¬A` falsch, und umgekehrt.
**Du** Und `False` ist wahrscheinlich die Aussage, die immer falsch ist?
**Robo** Ja, richtig.
**Du** Ist das jetzt nicht doch wieder trivial?
**Robo** Probier mal!"
trivial
Conclusion
"
Die Schwester lacht und eilt ihrem Bruder hinterher.
"
NewTactic trivial
DisabledTactic tauto
@@ -0,0 +1,45 @@
import Adam.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight
Game "Adam"
World "Proposition"
Level 7
Title "Aus Falschem folgt vieles."
Introduction
"
Als nächstes kommen drei Querulanten. Der erste hat folgendes Problem:
"
Statement ""
(A : Prop) (h : False) : A := by
Hint "**Du** Wenn ich das jetzt richtig lese, ist `{A}` eine Aussage,
und wir haben außerdem eine Annahme names `{h}`, die besagt …
**Robo** … die besagt, dass `False` gilt.
**Du** Ich dachte, `False` gilt nie?
**Robo** Ja, genau. Die Annahme ist `False`, also falsch.
Und aus einer falschen Annahme kann man bekanntlich alles beweisen!
Insbesondere die gesuchte Aussage `{A}`.
**Du** Und wie erkläre ich das jetzt diesem Formalosophen?
**Robo** Ich glaube, Du musst ihn darauf hinweisen, dass zwischen der allgemeingültigen
Annahme `True` und seiner Annahme `False` ein Widerspruch besteht. Probier mal `contradiction`."
contradiction
Conclusion
"Der erste Querulant ist offenbar zufrieden.
**Du** War das jetzt ein Widerspruchsbeweis?
**Robo** Nein, nein, ein Widerspruchsbeweis sieht anders aus. Das Argument hier war:
wir haben eine `contradiction` in unserem Annahmen, also folgt jede beliebige Aussage.
"
NewTactic contradiction
DisabledTactic tauto
@@ -0,0 +1,39 @@
import Adam.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight
import Adam.ToBePorted
Game "Adam"
World "Proposition"
Level 8
Title "Aus Falschem folgt vieles."
Introduction
"
Auftritt zweiter Querulant.
"
Statement ""
(n : ℕ) (h : n ≠ n) : n = 37 := by
Hint "**Du** Ist `{n} ≠ {n}` nicht auch ein Widerspruch?
**Robo** Probiers mal!"
contradiction
Conclusion
"
**Du** Ja, scheint funktioniert zu haben.
**Du** Aber irgendwie kommt mir das immer noch ein wenig suspekt vor.
Jetzt habe ich bewiesen, dass eine beliebige natürliche Zahl gleich 37 ist?
**Robo** Nein, nicht doch. Nur eine beliebige Zahl, die ungleich sich selbst ist, ist gleich 37.
Und gleich 38, und gleich 39, …
**Du** Ok, ok, verstehe.
"
NewTactic contradiction
DisabledTactic tauto
@@ -0,0 +1,35 @@
import Adam.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight
import Adam.ToBePorted
Game "Adam"
World "Proposition"
Level 9
Title "Aus Falschem folgt vieles."
Introduction
"
Auftritt dritter Querulant.
"
Statement ""
(n : ℕ) (h : n = 10) (g : n ≠ 10) : n = 42 := by
Hint "**Du** Wieder ein Widerspruch in den Annahmen?
**Robo** Ich sehe, du hast langsam den Dreh raus."
contradiction
Conclusion
"
**Robo** Gut gemacht. Bei dieser Frage ist auch ein bisschen offensichtlicher,
worin der Widerspruch besteht: Die Annahme `n ≠ 10` ist genau die Negation von `n = 10`.
Man muss `≠` immer als `¬(· = ·)` lesen.
"
NewTactic contradiction
DisabledTactic tauto
@@ -0,0 +1,70 @@
import Adam.Metadata
import Std.Tactic.RCases
set_option tactic.hygienic false
Game "Adam"
World "Proposition"
Level 10
Title "Und"
Introduction
"
Der nächste Formalosoph in der Reihe hat seine Frage bereìts mitgebracht.
Er legt sie uns vor, setzt sich hin und häkelt.
"
Statement "" (A B : Prop) (hA : A) (hB : B) : A ∧ B := by
Hint
"
**Du**: Also, wir haben zwei Annahmen: `{A}` gilt, und `{B}` gilt auch. Und beweisen sollen wir
dass `{A} und {B}` gilt. Ich glaube, diese Formalospinner treiben mich noch zur Verzweiflung.
Kann ich nicht wieder `trivial` sagen?
**Robo**: Nee, diesmal wird das nicht funktionieren.
Du musst das Beweisziel einfach in zwei Teile zerlegen. Probier mal `constructor`.
**Du**: Du meinst, `destructor`??
**Robo**: Nein, `constructor`. Ich weiß das ist verwirrend,
aber die nennen das hier so weil man die Aussage aus mehreren Teilen
konstruieren kann.
"
constructor
-- TODO: (BUG) Hier werden beide der folgenden Hints angezeigt.
-- Das logiste Verhalten wäre, wenn jeder nur am richtigen Ort
-- angezeigt würde.
-- Ein cooles Feature wäre, wenn man nur diesen ersten Hint schreiben könnte
-- und dieser dann automatisch auch beim zweiten Goal angezeigt wird, aber dann mit `B` statt `A`.
Hint (hidden := true)
"
**Robo**: Schau mal, das ist Zauberpapier.
Jetzt haben wir auf einmal zwei Beweisziele.
Hier ist dast Ziel `{A}`.
Ich glaube, Du weißt schon, wie man die jeweils erreicht.
Die Ziele stehen ja jeweils in den *Annahmen*.
"
assumption
Hint (hidden := true)
"
**Robo**: Schau mal, das ist Zauberpapier.
Jetzt haben wir auf einmal zwei Beweisziele.
Hier ist dast Ziel `{B}`.
Ich glaube, Du weißt schon, wie man die jeweils erreicht.
Die Ziele stehen ja jeweils in den *Annahmen*.
"
assumption
Conclusion
"
**Robo** Super!
Ihm scheinen diese Fragen inzwischen Spaß zu machen.
**Robo** Meinst Du, dieser Hebel, an dem \"Editor mode\" steht, ist echt?
Oder ist der nur gemalt? Probier mal!
"
NewTactic constructor
DisabledTactic tauto
@@ -0,0 +1,44 @@
import Adam.Metadata
import Std.Tactic.RCases
set_option tactic.hygienic false
Game "Adam"
World "Proposition"
Level 11
Title "Und"
Introduction
"
Langsam wird die Schlange kürzer. Die nächste Formalosophin, ebenfalls häkelnd, hat folgendes Anliegen.
"
Statement ""
(A B C : Prop) (h : A ∧ (B ∧ C)) : B := by
Hint "
**Du** Jetzt müssen wir wohl die Annahme de-konstruieren.
**Robo** Ja, genau. Das geht am einfachsten mit `rcases {h} with ⟨h₁, h₂⟩`.
**Du** Moment, wie schreib ich *das* denn hier auf?
**Robo** Die bleiden Klammern schreibst Du als `\\<` und `\\>`, oder gleichzeitig als `\\<>`.
Und h₁ schreibst Du einfach als `h\\1`. Aber Du kannst Dir auch einfach andere Namen
für `h₁` und `h₂`, zum Beispiel `rcases {h} with ⟨hA, hBC⟩`
"
Branch
rcases h with ⟨h₁, h₂⟩
Hint "**Robo** Das sieht doch schon besser aus! Gleich nochmal!"
rcases h with ⟨_, ⟨g , _⟩⟩
Hint (hidden := true) "**Robo** Du hast einen Beweis dafür in den *Annahmen*."
assumption
Conclusion
"
**Robo** Du hättest das übrigens auch direkt verschachtelt schreiben können:
`rcases h with ⟨h₁, ⟨h₂ , h₃⟩⟩`.
"
NewTactic rcases
DisabledTactic tauto
@@ -0,0 +1,42 @@
import Adam.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight
--set_option tactic.hygienic false
Game "Adam"
World "Proposition"
Level 12
Title "Oder"
Introduction
"
Der nächste bitte …
"
Statement
""
(A B : Prop) (hA : A) : A ∨ (¬ B) := by
Hint "**Du** Muss ich jetzt wieder das Beweisziel de-konstruieren?
**Robo** Nein, viel einfacher. Wenn Du eine Oder-Aussage beweisen sollst, musst Du Dich
einfach entscheiden, ob Du die linke oder rechte Seite beweisen willst.
**Du** Und wie erkläre ich meinem Formalosophen, welche Seite ich gern beweisen würde?
Ich will natürlich `{A}` beweisen!
**Robo** Mit `left` bzw. `right`. Ist doch logisch, oder?"
Branch
right
Hint "**Robo** Wusste gar nicht, dass Du eine Links-Rechts-Schwäche hast. Probier's nochmal."
left
assumption
Conclusion
"
Auch dieser Formalosoph zieht zufrieden von dannen.
"
NewTactic left right assumption
DisabledTactic tauto
@@ -0,0 +1,112 @@
import Adam.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight
set_option tactic.hygienic false
--set_option autoImplicit false
Game "Adam"
World "Proposition"
Level 13
Title "Oder"
Introduction
"
Der nächste bitte …
"
Statement ""
(A B : Prop) (h : (A ∧ B) ∨ A) : A := by
Hint "**Robo** Schau mal, wenn du mit dem Finger eine Annahme berührst, zeigt es dir,
wie die Klammern gesetzt sind. Irre…
**Du** Ah ich sehe, also `({A} ∧ {B}) ∨ {A}`!
**Du** Ich glaube den ganzen Zircus hier langsam nicht mehr:
Zuerst ein \"Und\" im Ziel, dann \"Und\" in der Annahme, dann \"Oder\" im Ziel und jetzt
\"Oder\" in der Annahme, die haben sich doch abgesprochen!
**Robo** Lass ihnen doch ihren Spaß.
Wir sind ja gleich hier fertig, und können zu einem interessanteren Planeten weiterfliegen.
**Du** Also, wieder `rcases …`?
**Robo** Ja, aber diesmal nicht `rcases {h} with ⟨h₁, h₂⟩`, sondern `rcases {h} with h | h`."
rcases h with h | h
Hint "**Robo**
Jetzt musst Du Dein Ziel zweimal beweisen:
Einmal unter Annahme der linken Seite `{A} ∨ {B}`,
und einmal unter Annahme der rechten Seite `{A}`.
Hier haben nehmen wir an, die linke Seite
sei wahr."
Hint (hidden := true) " **Robo** Wie man mit einem Und in den Annahmen umgeht,
weißt Du doch schon:
`rcases h with ⟨h₁, h₂⟩`. Zur Erinnerung: Für die Klammern schreibst Du `\\<>`."
rcases h with ⟨h₁, h₂⟩
Hint "**Robo** Jetzt musst Du Dein Ziel zweimal beweisen:
Einmal unter Annahme der linken Seite `{A}`,
und einmal unter Annahme der rechten Seite `{A} ∨ {B}`. Hier haben nehmen wir an, die linke Seite
sei wahr."
assumption
assumption
Conclusion
"**Du** Ok, das scheint ihn zufriedenzustellen. Nur noch eine Seele…
Kannst Du mir vorher noch einmal kurz alles Leansch zusammenfassen,
das Du mir bis hierher beigebracht hast?
Robo strahlt überglücklich. Noch *nie* warst Du so auf ihn angewiesen.
**Robo** Na klar, schau her!
## Notationen / Begriffe
| | Beschreibung |
|:--------------|:-------------------------------------------------------------------------|
| *Goal* | Was aktuell zu beweisen ist. |
| *Annahme* | Objekte & Resultate, die man zur Verfügung hat. |
| *Taktik* | Befehl im Beweis. Entspricht einem Beweisschritt. |
| `ℕ` | Typ aller natürlichen Zahlen. |
| `0, 1, 2, …` | Explizite natürliche Zahlen. |
| `=` | Gleichheit. |
| `≠` | Ungleichheit. Abkürzung für `¬(·=·)`. |
| `Prop` | Typ aller logischen Aussagen. |
| `True` | Die logische Aussage `(True : Prop)` ist bedingungslos wahr. |
| `False` | Die logische Aussage `(False : Prop)` ist bedingungslos falsch. |
| `¬` | Logische Negierung. |
| `∧` | Logisch UND. |
| `∨` | Logisch ODER. |
| `(n : ℕ)` | Eine natürliche Zahl. |
| `(A : Prop)` | Eine logische Aussage. |
| `(ha : A)` | Ein Beweis, dass die logische Aussage `(A : Prop)` wahr ist. |
| `(h : A ∧ B)` | Eine Annahme, die den Namen `h` bekommen hat. |
| `⟨·,·⟩` | Schreibweise für Struktur mit mehreren Feldern (kommt später im Detail). |
| `h.1, h.2, …` | Die einzelnen Felder der Stuktur. Auch `h.[Name des Feldes]` |
## Taktiken
Die Worte, die Du aktiv gebrauchen musst, heißen zusammengefasst `Taktiken`. Hier sind alle Taktiken, die wir auf diesem Planeten gebraucht haben:
| | Taktik | Beispiel |
|:---|:--------------------------|:--------------------------------------------------|
| 1 | `rfl` | Beweist `A = A`. |
| 2 | `assumption` | Sucht das Goal in den Annahmen. |
| 3 | `contradiction` | Sucht einen Widerspruch. |
| 4 | `trivial` | Kombiniert die obigen drei Taktiken (und mehr). |
| 5 | `constructor` | Teilt ein UND im Goal auf. |
| 6 | `left`/`right` | Beweist eine Seite eines ODER im Goal. |
| 7ᵃ | `rcases h with ⟨h₁, h₂⟩` | Teilt ein UND in den Annahmen auf. |
| 7ᵇ | `rcases h with h \\| h` | Teilt ein ODER in den Annahmen in zwei Fälle auf. |
**Du** Woher weißt Du das eigentlich alles?
**Robo** Keine Ahnung. War, glaube ich, vorinstalliert.
"
NewTactic assumption rcases
DisabledTactic tauto
@@ -0,0 +1,117 @@
import Adam.Metadata
import Std.Tactic.RCases
import Mathlib.Tactic.LeftRight
set_option tactic.hygienic false
Game "Adam"
World "Proposition"
Level 14
Title "Zusammenfassung"
Introduction
"
Der letzte Untertan tritt vor. Ihr Anliegen ist etwas komplizierter als die vorherigen.
**Robo** Wirf einfach alles drauf, was Du gelernt hast.
Hier, ich bin sogar so nett und zeig Dir noch einmal die vier
wichtigsten Taktiken für diese Situation an.
| (Übersicht) | Und (`∧`) | Oder (`∨`) |
|-------------|:-------------------------|:------------------------|
| Annahme | `rcases h with ⟨h₁, h₂⟩` | `rcases h with h \\| h` |
| Goal | `constructor` | `left`/`right` |
"
-- Note: The other direction would need arguing by cases.
Statement ""
(A B C : Prop) (h : A ∨ (B ∧ C)) : (A ∨ B) ∧ (A ∨ C) := by
Hint (hidden := true)
"**Robo**: Ich würd zuerst die Annahme {h} mit `rcases {h}` aufteilen."
Branch
constructor
· rcases h with h' | h'
· left
assumption
· rcases h' with ⟨h₁, h₂⟩
right
assumption
· rcases h with h' | h'
· left
assumption
· rcases h' with ⟨h₁, h₂⟩
right
assumption
rcases h
Hint (hidden := true) "**Robo**: Jetzt kannst Du das `∧` im Goal mit `constructor` angehen."
· constructor
· left
assumption
· left
assumption
· Hint (hidden := true)
"**Robo**: Hier würde ich die Annahme {h} nochmals mit `rcases` aufteilen."
Branch
constructor
· Hint "**Robo**: Der Nachteil an der Reihenfolge ist, dass du jetzt in jedem Untergoal
`rcases h` aufrufen musst."
Branch
right
rcases h with ⟨h₁, h₂⟩
assumption
rcases h with ⟨h₁, h₂⟩
right
assumption
· Branch
right
rcases h with ⟨h₁, h₂⟩
assumption
rcases h with ⟨h₁, h₂⟩
right
assumption
rcases h with ⟨h₁, h₂⟩
constructor
· right
assumption
· right
assumption
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : B ∧ C) : (A ∨ B) ∧ (A ∨ C) =>
-- "**Robo** Das `∧` in der Annahme kannst Du mit `rcases {h} with ⟨h₁, h₂⟩` zerlegen."
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) : (A ∨ B) ∧ (A ∨ C) =>
-- "**Robo** Das `∧` im Goal kannst Du mit `constructor` zerlegen."
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : A ∨ (B ∧ C)) : (A ∨ B) ∧ (A ∨ C) =>
-- "**Robo** Das `∨` in der Annahme kannst Du mit `rcases h with h | h` zerlegen."
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : A ∨ (B ∧ C)) : (A ∨ B) =>
-- "**Robo** Das `∨` in der Annahme kannst Du mit `rcases h with h | h` zerlegen."
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : A ∨ (B ∧ C)) : (A ∨ C) =>
-- "**Robo** Das `∨` in der Annahme kannst Du mit `rcases h with h | h` zerlegen."
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : B ∧ C) : (A ∨ B) =>
-- "**Robo** Das `∧` in der Annahme kannst Du mit `rcases h with ⟨h₁, h₂⟩` zerlegen."
-- HiddenHint (A : Prop) (B : Prop) (C : Prop) (h : B ∧ C) : (A ∨ C) =>
-- "**Robo** Das `∧` in der Annahme kannst Du mit `rcases {h} with ⟨h₁, h₂⟩` zerlegen."
-- -- TODO: Hint nur Anhand der Annahmen?
-- HiddenHint (A : Prop) (B : Prop) : A ∨ B =>
-- "**Robo** `left` oder `right`?"
Conclusion
"
**Robo** Bravo! Jetzt aber nichts wie weg hier, bevor sich eine neue Schlange bildet!
Königin *Logisinde* ist in der Zwischenzeit eingeschlafen, und ihr stehlt euch heimlich davon.
"
NewTactic left right assumption constructor rcases rfl contradiction trivial
DisabledTactic tauto