Hangman

Dit is een eenvoudig spelletje. Het is gemaakt door Dan Wagoner in 2009, maar ik heb het eenvoudiger gemaakt. Ook heb ik het geschikt gemaakt voor 1602 displays met een I²C interface. Met 1602 wordt aangegeven dat het een tekstdisplay is met 2 regels van 16 tekens. Als je een ander tekstscherm hebt, zoals bijvoorbeeld een 2004 display dan zal het waarschijnlijk meteen goed werken, maar je kunt het spel dan nog mooier maken omdat je dan meer ruimte op het display hebt.
Je hebt naast draadjes e.d. nog de volgende onderdelen nodig: Als je een halfsize breadboard gebruikt dan moet je alles netjes positioneren, bijvoorbeeld zoals op de foto.

Sluit e.e.a. als volgt aan: Het programma gebruikt de bibliotheek LiquidCrystal_I2C_Hangul.h. Deze is gemakkelijk te installeren via bibliotheekbeheer. Ik heb in het programma als adres 0x3F opgegeven, maar soms moet je adres 0x27 gebruiken! Als je geen beeld krijgt, ook als je aan de knop op de interface draait, verander 0x3F dan in 0x27.
In het programma zijn maar enkele woorden opgenomen, maar dat is niet erg als je iemand anders laat spelen die de woorden niet kent. Als je veel meer woorden wilt opnemen, dan moet je allerlei wijzigingen aanbrengen, want de Arduino heeft hier te weinig geheugen voor. Om enigszins ruimte te besparen heb ik waar mogelijk de tekst in F() gezet, hierdoor komt de tekst in het programmageheugen (ook wel flash memory of PROGMEM genoemd).

Speel het spel

Speel het spel als volgt:
Draai aan de knop van de potentiometer tot je de letter die je wil proberen ziet binnen de driehoekjes en druk vervolgens op de knop.
Als de letter in het woord voorkomt dan wordt dat weergegeven en anders wordt een volgend deel van je lichaam aan de galg gehangen.
Als je een letter al gekozen hebt, dan zie je een * en kun je de letter niet nogmaals kiezen. Als je lichaam helemaal aan de galg hangt kun je nog steeds proberen het woord te vinden, maar heb je een verkeerde letter dan "hang je". Het spel wordt vervolgens helemaal opnieuw gestart.

Programmacode

/* Hangman
 Speel hangman op je Arduino. Er is een kleine lijst met woorden waaruit het programma een 
 willekeurig woord kiest.
 Je kiest een letter door aan de draaiknop te draaien en vervolgens op de drukknop te drukken.
 Letters die al geraden zijn worden vervangen door een *. Je kunt deze niet opnieuw kiezen. 
 Als je helemaal aan de galg hangt en je raadt het woord vervolgens niet dan is het programma over.
 Je ziet dan welk woord je had moeten vinden. As je het woord wel op tijd raad dan komt een eindscherm.
 Het programma start vervolgens opnieuw op. 

 Geschreven door Dan Wagoner (2009)
 Eenvoudiger versie door Th.M. Hupkens 2022 */

#include <LiquidCrystal_I2C_Hangul.h>
LiquidCrystal_I2C_Hangul lcd(0x3F, 16, 2); // adres (kan ook 0x27 zijn), aantal kolommen, aantal rijen
#define AantalWoorden 12
#define Drukknop  2
#define POTPIN A0

void(* Reset) (void) = 0; // Dit is een trucje om de Arduino te resetten

const String woorden[] = { "kunstwerk", "atmosfeer", "letterlijk", "vlaggenmast", "eindstreep", "wetenschap", 
"wiskunde", "mensaap", "dikkopje", "zwaardvis", "interface", "barokperiode" };

char abc[] = "abcdefghijklmnopqrstuvwxyz";
char gokLetter;
char laatsteLetter;
int wordSize;
int hangman = 0;
int totaalGoed = 0;
int sel = 0;
unsigned long lastDebounceTime = 0;
volatile boolean Druk = false; // true als de knop is ingedrukt

String Woord;

// hangman onderdelen
byte topL[]   = { 31, 16, 16, 16, 16, 16, 16, 16 };
byte topR[]   = { 28,  4,  4,  0,  0,  0,  0,  0 };
byte onderL[] = { 16, 16, 16, 16, 16, 31, 31, 31 };
byte onderR[] = {  0,  0,  0,  0,  0,  0,  0,  0 };
byte hoofd[]  = { 28,  4,  4, 14, 14,  0,  0,  0 };
byte borst[]  = { 28,  4,  4, 14, 14,  4,  4,  4 };
byte buik[]   = { 4,   4,  0,  0,  0,  0,  0,  0 };
byte armR[]   = { 28,  4,  4, 14, 14,  5,  6,  4 };
byte armL[]   = { 28,  4,  4, 14, 14, 21, 14,  4 };
byte beenR[]  = {  4,  4,  2,  2,  1,  0,  0,  0 };
byte beenL[]  = {  4,  4, 10, 10, 17,  0,  0,  0 };
byte pijlL[]  = { 16, 24, 28, 30, 30, 28, 24, 16 };
byte pijlR[]  = {  1,  3,  7, 15, 15,  7,  3,  1 };

void setup()
{
  lcd.init();
  lcd.setBacklight(HIGH);
  lcd.clear();
  lcd.setCursor(4, 0);
  lcd.print(F("HANGMAN"));
  lcd.setCursor(0, 1);
  lcd.print(F("START: druk knop"));
  delay(2000);
  // Maak de galg en het poppetje dat eraan hangt
  lcd.createChar(0, topL);
  lcd.createChar(1, topR);
  lcd.createChar(2, onderL);
  lcd.createChar(3, onderR);
  lcd.createChar(4, pijlL);
  lcd.createChar(5, pijlR);
  pinMode(Drukknop, INPUT_PULLUP); // Verbind met aarde als ingedrukt
  while (digitalRead(Drukknop)); // Doe niets zolang de drukknop niet is ingedrukt 
  randomSeed(millis()); // Stel de random generator in via verstreken tijd (beter dan via ruis op een anloge pin)
  kiesWoord();
  tekenGalg();
  attachInterrupt(digitalPinToInterrupt(Drukknop), ZetLetter, FALLING);
}

void ZetLetter() {
  if (gokLetter == '*') return;
  Druk = true;
}

void kiesWoord() {
  Woord = woorden[random(AantalWoorden)]; // Kies een woord uit de lijst
  wordSize = Woord.length();
}

void tekenGalg() { // lege galg
  lcd.clear();
  lcd.write(byte(0));
  lcd.write(1);
  lcd.setCursor(0, 1);
  lcd.write(2);
  lcd.write(3);
  lcd.setCursor(3, 1);
  for (int x = 0; x < wordSize; x++) {
    lcd.print("_"); // Teken voor elke letter een streepje
  }
}

void loop() {
  int potVal = analogRead(POTPIN)/40; // Selecteer letter via potentiometer (1024/26 is ongeveer 40)
  gokLetter = abc[potVal];
  if (gokLetter != laatsteLetter) { // Print alleen als er iets veranderd is
    laatsteLetter = gokLetter;  
    lcd.setCursor(3, 0); // Print letters links van de geselecteerde letter
    for (int x = 5; x >= 1 ; x--) {
      if (potVal - x >= 0) lcd.print(char(potVal - x + 97));
      else lcd.print(" ");
    }
    lcd.write(4); // print linker pijl |>
    lcd.setCursor(9, 0);
    lcd.print(gokLetter);
    abc[potVal];
    lcd.write(5); // print rechter pijl <|
    lcd.setCursor(11, 0); // Print letters rechts van de geselecteerde letter
    for (int x = 1; x <= 5 ; x++) {
      if (potVal + x <= 25) lcd.print(char(potVal + x + 97));
      else lcd.print(" ");
    }
  }
  bool Geraden = false;
  if (Druk) {
    for (byte letter = 0; letter < wordSize; letter++)
      if (Woord[letter] == gokLetter) {
        lcd.setCursor(letter + 3, 1);
        lcd.print(gokLetter);
        totaalGoed++;
        abc[potVal] = '*';
        Geraden = true;
      }
    if (!Geraden) {
      hangman++;
      draw_hangman(hangman);
    }
    Druk = false;
  }
  if (totaalGoed == wordSize) {
    gameOver(1);
  }
}

void draw_hangman(int var) {
  switch (var) {
    case 1:
      lcd.createChar(1, hoofd);           
      break;
    case 2:
      lcd.createChar(1, borst); 
      lcd.createChar(3, buik);
      break;
    case 3:
      lcd.createChar(1, armR);       
      break;
    case 4:
      lcd.createChar(1, armL);        
      break;
    case 5:
      lcd.createChar(3, beenR);       
      break;
    case 6:
      lcd.createChar(3, beenL);        
      break;
    case 7:
      gameOver(0);
    default:
      break;
  }
}

void gameOver(int WatDoen) {
  switch (WatDoen) {
    case 0:  // Verliezer
      lcd.clear();
      lcd.setCursor(3, 0);
      lcd.print(F("GAME OVER"));
      lcd.setCursor(int((16 - wordSize)/2), 1);
      lcd.print(Woord);
      break;
    case 1:  // Winnaar
      delay(1000);
      lcd.clear();
      lcd.setCursor(1, 0);
      lcd.print(F("Gefeliciteerd"));
      lcd.setCursor(4, 1);
      lcd.print(F("Geraden !"));
  }
  delay(2000);
  Reset();
}