Bush Wanderer - code intended for teaching I am teaching computing to an AS class and this was their assignment:


  Bush Wanderer
  
  Task 1
  
  Create a 5x5 grid of hashes(#) where the player is an x on the
  board
  
  Prompt the player to move in either N, E, S or W direction and then
  reprint the board to show the new position.
  
  Example:
  
  x # # # #
# # # # # 
# # # # #
# # # # #
# # # # #
Which way would you like to move?(N,E,S or W) 

  
  Task 2
  
  
  Ensure the player does not move off of the board
  Place a hidden chest somewhere on the board at random
  End the game when the chest is found
  Tell the player how many moves it took to find the chest
  Add a visible (but blind) dragon that can teleport around the board    at random and will eat you and end your game if it gets you before
  you get the chest
  


This was my exemplar solution at the end and I wanted to get some feedback on what the professional community thought of it. How could this be improved?



import random

grid = 5

moves = 0

x = 0
y = 0

cx = random.randint(0,(grid-1))
cy = random.randint(0,(grid-1))

while(True):

  dx = random.randint(0,(grid-1))
  dy = random.randint(0,(grid-1))

  for i in range(grid):
      if i == dy and i == y:
        for u in range(grid):
          if u == dx:
            print('@ ', end='')
          elif u == x:
            print('x ', end='')
          else:
            print('# ', end='')
        print()
      elif i == y:
          print('# '*x, end="")
          print('x ', end="")
          print('# '*((grid-x)-1))
      elif i == dy:
          print('# '*dx, end="")
          print('@ ', end="")
          print('# '*((grid-dx)-1))
      else:  
        for i in range(grid):
          print('# ', end="")
        print()

  if dx == x and dy == y:
    if moves == 1:
      print('You died in', moves,'move!')
    elif moves &gt; 1 or moves == 0:
      print('You died in', moves,'moves!')
    break
  elif cx == x and cy == y:
    if moves == 1:
      print('You won in', moves,'move!')
    elif moves &gt; 1 or moves == 0:
      print('You won in', moves,'moves!')
    break

  m = input('Which way would you like to move?(N,E,S or W)').lower()

  if m == 'n' and y &gt; 0:
      y = y-1
  elif m == 'e' and x &lt; (grid-1):
      x = x+1
  elif m == 's' and y &lt; (grid-1):
      y = y+1
  elif m == 'w' and x &gt;0:
      x = x-1
  else:
      print('Invalid move')

  moves = moves + 1
Because the code you write is to be used for teaching, the importance is not on performance, but on correctness and readability and understandability (comphrensiveness?).

So basically...


Add comments! Explain each step, so that the kids are gonna see the code, think "what does it do", and they can read it right there!
Don't use dx and dy, use more explanatory variables!


Let's combine them:

  if dx == x and dy == y:
    if moves == 1:
      print('You died in', moves,'move!')
    elif moves &gt; 1 or moves == 0:
      print('You died in', moves,'moves!')
    break
  elif cx == x and cy == y:
    if moves == 1:
      print('You won in', moves,'move!')
    elif moves &gt; 1 or moves == 0:
      print('You won in', moves,'moves!')
    break


This is hard to understand. dx and dy were hard to grasp at first - when doing games programming, usually dx and dy are "delta-x" and "delta-y", to calculate object velocity.

But it's dragon x y and chest x y here!

So just call it that.

  if dragon_x == x and dragon_y == y:
    if moves == 1:
      print('You died in', moves,'move!')
    elif moves &gt; 1 or moves == 0:
      print('You died in', moves,'moves!')
    break
  elif chest_x == x and chest_y == y:
    if moves == 1:
      print('You won in', moves,'move!')
    elif moves &gt; 1 or moves == 0:
      print('You won in', moves,'moves!')
    break


Next, I'd usually tell you to split this up into functions. But maybe these kids don't know functions yet, and it's remarkably frustrating to be shown "the solution" when "the solution" consists of "this magic trick I didn't teach you yet, haha".

Use comments instead, and be sure to teach them what a comment is and what its for. If you have to review homework, a comment might be all you get to understand their code.

  #check for loss by comparing dragon and player location
  if dragon_x == x and dragon_y == y:
    if moves == 1:
      print('You died in', moves,'move!')
    elif moves &gt; 1 or moves == 0:
      print('You died in', moves,'moves!')
    break
  #else, check for victory by comparing chest and player location
  elif chest_x == x and chest_y == y:
    if moves == 1:
      print('You won in', moves,'move!')
    elif moves &gt; 1 or moves == 0:
      print('You won in', moves,'moves!')
    break


Note that function names make good comments. Instead of checkForVictory() we just put #check for victory. I recommend you start comments with why, and then with how. How-comments should eventually be removed (you can read the code), and why-comments should stay, but when learning how to program, having the explanation next to the code just helps. It also promotes micro problem-solution flow (in my opinion), which is what you need later if you want to split up code into smaller chunks or problems.

And this loop here...

  for i in range(grid):
      if i == dy and i == y:
        for u in range(grid):
          if u == dx:
            print('@ ', end='')
          elif u == x:
            print('x ', end='')
          else:
            print('# ', end='')
        print()
      elif i == y:
          print('# '*x, end="")
          print('x ', end="")
          print('# '*((grid-x)-1))
      elif i == dy:
          print('# '*dx, end="")
          print('@ ', end="")
          print('# '*((grid-dx)-1))
      else:  
        for i in range(grid):
          print('# ', end="")
        print()


Is mighty confusing. There is no easily discernible pattern to it. If you had to explain the thought process that went into each step, what would you say? (Teacher, I don't understand, how did you create this?)

Here's mine:

per tile:
  print dragon if dragon is on tile
  print player if player is on tile
  print empty tile if nobody is on tile


Those are the requirements, next up is realizing that if we have a grid, we ought to do it by row and columns:

for each row
  for each column
    if the dragon is on that tile
     print dragon
    else if the player is on that tile
     print player
    else
     print empty tile
  print new line, to go to next row


And then translate to python (untested)

for i in range(grid)
  for u in range(grid)
    if i == dragon_y and u == dragon_x:
     print('@ ', end='')
    elif i == player_y and u == player_x:
     print('x ', end='')
    else:
     print('# ', end='')
  print()


Another reason for this is that if you were to get a question like, "how do we add mountains, where the dragon can wander, but the player can't?", you'd be stumped. Or at least I would be, if I had to add mountains into your display code. It's all about clever tricks with multiplying strings. You'd have to alter ALL your cases. Or if there were two dragons, because, you know, hard mode. Again, it would take you a major overhaul.

I'd just make a simple change:

for each row
  for each column
    if there is a dragon on that tile
     print dragon
    else if the player is on that tile
     print player
    else if that tile is a mountain
     print mountain
    else
     print empty tile
  print new line, to go to next row


and it supports mountains and multiple dragons!

Possibly the best thing you could do is take another look at your code, and remove the python element from it. Start in English, and convert that to python. I bet that's what the kids will be doing. 

Using an approach like this, the code is going to be a lot shorter and easy to understand. Yes, it's slower by doing all the checks all the time. But for these kids, it's probably going to be more important that they understand the code, than that it runs three times as fast. Computers these days are forgiving, and you can always teach them better optimizations later.

Plus, if they were to shy away from programming because the code is too complex, that'd be a darn shame.

And get comfortable with your code before the lesson. It would be best if you could do live editing in the classroom.



As for industry standards...

  for i in range(grid):
      if i == dy and i == y:
        for u in range(grid):
          if u == dx:
            print('@ ', end='')
          elif u == x:
            print('x ', end='')
          else:
            print('# ', end='')
        print()
      elif i == y:
          print('# '*x, end="")
          print('x ', end="")
          print('# '*((grid-x)-1))
      elif i == dy:
          print('# '*dx, end="")
          print('@ ', end="")
          print('# '*((grid-dx)-1))
      else:  
        for i in range(grid):
          print('# ', end="")
        print()


Extract to printGrid.

  m = input('Which way would you like to move?(N,E,S or W)').lower()

  if m == 'n' and y &gt; 0:
      y = y-1
  elif m == 'e' and x &lt; (grid-1):
      x = x+1
  elif m == 's' and y &lt; (grid-1):
      y = y+1
  elif m == 'w' and x &gt;0:
      x = x-1
  else:
      print('Invalid move')

  moves = moves + 1


Extract to pickMove, and use a while loop to make sure that the move counter (moves) doesn't go up if you make a bad move.

Encapsulate conditionals by putting checks like dy == y and dx == x into either a function isDragonEatingPlayer (naming is hard) or just a temporary variable of some sorts.