(-: Fibergeek's Blog :-)
Inhoud blog
  • Python 2 of 3
  • IDA-python: GetStructureIdAt
  • Geslaagd in Microsoft's 70-680 examen
  • minibio: kort vervolg
  • miniboa: een voorbeeld
    Zoeken in blog

    Categorieën
  • Code: C/C++ (2)
  • Code: Powershell (1)
  • Code: Python (8)
  • Code: WPF (2)
  • Programmeren (5)
  • 05-07-2011
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.Hoger Lager herdaan

    Hoger Lager; toen mijn kameraad op school mij leerde programmeren, heeft hij mij IF's uitgelegd via het spelletje hoger-lager. Hoger Lager is dan ook het eerste spelleke da'k heb geschreven. Nu 20 jaar later (ik ben er 34) wil ik dit spel eens opnieuw schrijven maar dan ondersteund met moderne technieken zoals Test-Units.

    20 jaar geleden begon ik met Basic, dit is mijn moderne Python-variant:

    import random

    getal = random.randint(0, 100)
    while True:
      invoer = int(input("Geef een getal van 0 tot 100: "))
      if invoer == getal:
        print "Goed zo!"
        break
      elif invoer < getal:
        print "Neen! Hoger!"
      else:
        print "Neen! Lager:"

    Na de eerste & tweede refactoring-sessies kwam ik tot deze versie (in het donkerrood gekleurd omdat de code maar tijdelijk is):

    import random

    def GenereerWillekeurigGetal(min, max):
      return random.randint(min, max)

    def PrintVraagEnLeesGetalUit(min, max):
      return int(input("Geef een getal van %d tot %d: " % (min, max)))

    def VerwerkEnVergelijkInvoer(IngevoerdGetal, TeRadenGetal):
      if IngevoerdGetal == TeRadenGetal:
        print "Goed zo!"
        return True
      elif IngevoerdGetal < TeRadenGetal:
        print "Neen! Hoger!"
      else:
        print "Neen! Lager:"
      return False

    def SpeelHogerLager():
      MinGetal = 0
      MaxGetal = 100
      TeRadenGetal = GenereerWillekeurigGetal(MinGetal, MaxGetal)
      while True:
        IngevoerdGetal = PrintVraagEnLeesGetalUit(MinGetal, MaxGetal)
        if VerwerkEnVergelijkInvoer(IngevoerdGetal, TeRadenGetal):
          break

    def main():
      SpeelHogerLager()

    if __name__ == "__main__":
      main()


    Volgens TDD (Test Driven Development) moeten we eerst een test schrijven en dan pas de code, maar goed, het spel Hoger Lager was er al. Dit is dus de testcode die ik geschreven heb om de bovenstaande spelcode te testen:

    from HogerLager import *
    import unittest

    class test_HogerLager(unittest.TestCase):
      def TestGenereerWillekeurigGetalMetMinMax(self, min, max):
        for j in range((max - min) * 10):
          getal = GenereerWillekeurigGetal(min, max)
          self.failIf(getal < min)
          self.failIf(getal > max)
     
      def test_GenereerWillekeurigGetal(self):
        for i in range(60):
          min = 50 - i
          max = 50 + i
          self.TestGenereerWillekeurigGetalMetMinMax(min, max)
     
      def test_VergelijkGetallen(self):
        self.assertFalse(VerwerkEnVergelijkInvoer(50, 49))
        self.assertTrue (VerwerkEnVergelijkInvoer(50, 50))
        self.assertFalse(VerwerkEnVergelijkInvoer(50, 51))

    Het staat me echter niet aan dat de bovenstaande testcode ook het antwoord (Hoger of Lager) afdrukt. Dit gebeurt indirect door VerwerkEnVergelijkInvoer. Daarom heb ik VerwerkEnVergelijkInvoer gesplitst:

    import random

    def GenereerWillekeurigGetal(min, max):
      return random.randint(min, max)

    def PrintVraagEnLeesGetalUit(min, max):
      return int(input("Geef een getal van %d tot %d: " % (min, max)))

    def BerekenVerschil(IngevoerdGetal, TeRadenGetal):
      return IngevoerdGetal - TeRadenGetal

    def VerwerkInvoer(IngevoerdGetal, TeRadenGetal, UitvoerActie):
      UitvoerActie(IngevoerdGetal, TeRadenGetal)
      return IngevoerdGetal == TeRadenGetal

    def PrintResultaat(IngevoerdGetal, TeRadenGetal):
      Verschil = BerekenVerschil(IngevoerdGetal, TeRadenGetal)
      if Verschil == 0:
        print "Goed zo!"
      elif Verschil < 0:
        print "Neen! Hoger!"
      else:
        print "Neen! Lager:"

    def VerwerkInvoerEnPrintResultaat(IngevoerdGetal, TeRadenGetal):
      return VerwerkInvoer(IngevoerdGetal, TeRadenGetal, PrintResultaat)

    def SpeelHogerLager():
      MinGetal = 0
      MaxGetal = 100
      TeRadenGetal = GenereerWillekeurigGetal(MinGetal, MaxGetal)
      while True:
        IngevoerdGetal = PrintVraagEnLeesGetalUit(MinGetal, MaxGetal)
        if VerwerkInvoerEnPrintResultaat(IngevoerdGetal, TeRadenGetal):
          break

    def main():
      SpeelHogerLager()

    if __name__ == "__main__":
      main()


    Tenslotte volgt de bijhorende testcode:

    from HogerLager import *
    import unittest

    def GeenUitvoerActie(a, b):
      pass

    class test_HogerLager(unittest.TestCase):
      def TestGenereerWillekeurigGetalMetMinMax(self, min, max):
        for j in range((max - min) * 10):
          getal = GenereerWillekeurigGetal(min, max)
          self.failIf(getal < min)
          self.failIf(getal > max)
     
      def test_GenereerWillekeurigGetal(self):
        for i in range(60):
          min = 50 - i
          max = 50 + i
          self.TestGenereerWillekeurigGetalMetMinMax(min, max)
     
      def test_BerekenVerschil(self):
        self.failUnless(BerekenVerschil(50, 49) >  0)
        self.failUnless(BerekenVerschil(50, 50) == 0)
        self.failUnless(BerekenVerschil(50, 51) <  0)
     
      def test_VerwerkInvoer(self):
        self.assertFalse(VerwerkInvoer(50, 49, GeenUitvoerActie))
        self.assertTrue (VerwerkInvoer(50, 50, GeenUitvoerActie))
        self.assertFalse(VerwerkInvoer(50, 51, GeenUitvoerActie))

    if __name__ == '__main__':
      unittest.main()


    Eindnota: Ik ben nog niet overtuigd dat deze laatste versie van Hoger Lager de beste is, want de hoofdlus SpeelHogerLager is verantwoordelijk voor zowel het spel, als de invoer en de uitvoer via de console. Daardoor is het onmogelijk de hoofdlus te testen via een unittest-test_XXX-functie. Het is beter om de invoer en uitvoer via polyformisme te implementeren, de hoofdlus kan dan vervolgens via "mocking" getest worden. Voor het polyformisme zou ik in .NET een Interface definiĆ«ren en gebruiken, in Python zal ik dit anders moeten oplossen (via een abstracte class). Ook de functie PrintVraagEnLeesGetalUit moet herbekeken worden want ze is zowat de enigste functie in het programma die een crash kan veroorzaken. Met name als een niet-cijfer ingevoerd wordt, zal de int-functieaanroep een exception triggeren. Deze "bug" bestaat ook in de eerste versie van het spel. Dit alles is voor een volgende blog.

    05-07-2011, 00:00 Geschreven door Fibergeek  


    Categorie:Code: Python
    Archief per week
  • 25/11-01/12 2013
  • 05/11-11/11 2012
  • 07/05-13/05 2012
  • 05/03-11/03 2012
  • 20/02-26/02 2012
  • 13/02-19/02 2012
  • 30/01-05/02 2012
  • 12/12-18/12 2011
  • 05/12-11/12 2011
  • 19/09-25/09 2011
  • 15/08-21/08 2011
  • 01/08-07/08 2011
  • 04/07-10/07 2011
  • 06/06-12/06 2011

    E-mail mij

    Druk op onderstaande knop om mij te e-mailen.


    Gastenboek

    Druk op onderstaande knop om een berichtje achter te laten in mijn gastenboek


    Blog als favoriet !


    Blog tegen de wet? Klik hier.
    Gratis blog op https://www.bloggen.be - Meer blogs