我正在为一门AS课程教授计算,这是他们的作业:


Bush Wanderer

任务1

创建5x5网格哈希值(#),其中播放器是
板上的x显示新位置。

示例:

 x # # # #
# # # # # 
# # # # #
# # # # #
# # # # #
Which way would you like to move?(N,E,S or W) 
 


任务2


确保玩家不会离开棋盘
将隐藏的箱子随机放在棋盘上的某个地方
找到箱子后结束游戏
告诉玩家找到箱子的动作
添加一条可见(但盲目的)的巨龙,它可以在板子上随机传送,并会吞噬你,并且如果能早于你就结束你的游戏
你会得到胸部



这是我最后的示例解决方案,我想买点饲料了解专业人士对此的看法。




 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 > 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 > 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 > 0:
      y = y-1
  elif m == 'e' and x < (grid-1):
      x = x+1
  elif m == 's' and y < (grid-1):
      y = y+1
  elif m == 'w' and x >0:
      x = x-1
  else:
      print('Invalid move')

  moves = moves + 1
 


评论

“ AS”类?速记代表什么?

高级子公司-英国12年级(16/17岁)

@TobyAdams感谢您的接受,我是否得到了您想要的一切?它并不是真正的“行业标准”视图,而是针对您将要教这些东西的想法而量身定制的。

@Pimgd用虚幻的方式反映了您的反馈意见,谢谢。我从没想到过在那种时间范围内会有如此高的反应。我现在要重新考虑一下,并结合您所建议的一些内容。

您的学生将按照您的示例进行操作,并避免使用注释和有意义的变量名。鼓励他们使用的一种好方法是让学生然后交易代码并对其进行修改以添加新功能。

#1 楼

因为您编写的代码将用于教学,所以重要性不在于性能,而在于正确性,可读性和可理解性(可理解性)。

所以基本上... />
添加评论!解释每个步骤,以便孩子们看到代码,思考“它做什么”,他们可以在那里阅读它!
不要使用dxdy,请使用更多解释性变量! >
让它们结合起来:

  if dx == x and dy == y:
    if moves == 1:
      print('You died in', moves,'move!')
    elif moves > 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 > 1 or moves == 0:
      print('You won in', moves,'moves!')
    break


这很难理解。 dxdy刚开始很难掌握-在进行游戏编程时,通常dxdy分别是“ delta-x”和“ delta-y”,以计算物体速度。

但是龙xy和

就这么称呼它。

  if dragon_x == x and dragon_y == y:
    if moves == 1:
      print('You died in', moves,'move!')
    elif moves > 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 > 1 or moves == 0:
      print('You won in', moves,'moves!')
    break


接下来,我通常会告诉您将其分解为功能。但是也许这些孩子还不了解功能,当“解决方案”由“我还没有教过的魔术,哈哈”组成时,向他们展示“解决方案”令人非常沮丧。

请改用评论,并确保教给他们评论是什么以及它的用途。如果您需要复习作业,那么可能只有注释才能理解它们的代码。

  #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 > 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 > 1 or moves == 0:
      print('You won in', moves,'moves!')
    break


请注意,函数名可以提供很好的注释。代替checkForVictory(),我们只需输入#check for victory。我建议您先评论为什么,然后再如何。最终应该删除how-comments(您可以阅读代码),而why-comments应该保留,但是在学习如何编程时,在代码旁边进行解释会很有帮助。它还促进了微观问题解决方案的流程(以我的观点),如果您想将代码分成较小的块或问题,则稍后需要使用此方法。

这里的循环...

  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()


令人困惑。没有容易辨认的模式。如果您必须解释每个步骤的思考过程,您会说什么? (老师,我不明白,您是如何创建的?)

这里是我的:

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

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


另一个原因是,如果您遇到一个问题,例如“我们如何添加山峰,龙可以在其中游荡,而玩家却不能?”被困住了。如果我必须在显示代码中添加山峰,或者至少是我会的。这都是关于用字符串相乘的巧妙技巧。您必须更改所有情况。或者,如果有两条龙,那是因为,您知道,是硬模式。再次,这将需要您进行大修。

我只需做一个简单的更改:

可能最好的办法是再检查一下代码,然后从其中删除python元素。以英语开始,然后将其转换为python。我敢打赌,那是孩子们会做的。

使用这样的方法,代码将变得更短,更易于理解。是的,一直进行所有检查会比较慢。但是对于这些孩子来说,理解代码可能比运行速度快三倍更为重要。这些天的计算机是宽容的,您以后可以随时教给他们更好的优化方法。 br />
在上课之前,请熟悉一下您的代码。最好在教室中进行实时编辑。


关于行业标准...

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()


提取到printGrid

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


提取到pickMove,并使用while循环确保如果移动不正确,移动计数器(moves)不会增加。通过将类似dy == y and dx == x的检查放入函数isDragonEatingPlayer(很难命名)或只是某种类型的临时变量中。

评论


\ $ \ begingroup \ $
建议将其拆分为功能。在编程教学中,回避函数似乎是“共同的主题”,这是完全不合理的,因为它是将复杂的事物隐藏在简单名称后的“主要思想”的一部分(抽象)。我知道使用函数(即使有时过分使用)的学生通常表现会好得多。
\ $ \ endgroup \ $
–Daniel Jour
16年2月27日在8:32

\ $ \ begingroup \ $
@DanielJour。如果我们不关心性能,是否有类似使用许多功能的东西?
\ $ \ endgroup \ $
– magu_
16年4月26日在15:28

\ $ \ begingroup \ $
@magu_我这么认为:一个函数应该完成一项工作。如果做得更多,请将其拆分;如果做得更少,则将其删除。
\ $ \ endgroup \ $
–Daniel Jour
16年4月29日在7:00

#2 楼

我主要的批评是,代码很难遵循,因为变量名是隐晦的,逻辑很沉重。

我理解您希望避免用语言功能压倒学生的渴望,但是在某些情况下,如果没有某些功能,代码将变得不符合Python规范,变得更加复杂。元组非常有用,因为xycxcydxdy既隐秘又麻烦。这会更好:

from random import randint

GRID_SIZE = 5

your_xy = (0, 0)
chest_xy = (randint(0, GRID_SIZE-1), randint(0, GRID_SIZE-1))


不幸的是,使用元组会使下一个坐标的构建更加难看,特别是如果您不使用namedtuple或此技巧来添加坐标。


印刷电路板程序是程序中最讨厌的部分:


  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()



如果您不尝试使用*运算符,将会简单很多:计数循环:


for y in range(GRID_SIZE):          # Note improved variable naming
    for x in range(GRID_SIZE):
        if (x, y) == dragon_xy:
            print('@ ', end='')
        elif (x, y) == your_xy:
            print('x ', end='')
        else:
            print('# ', end='')
    print()



计数循环几乎总是最好使用某种range()enumerate()itertools编写。在这种情况下,请使用itertools.count()

请注意,即使移动无效,您也可以增加计数。我不知道这是否是故意的。


还有一些简单但重要的要点。

通过PEP 8,缩进应为四个空格。这是Python中的一个重要约定,因为缩进非常重要。

您可能已经注意到,GRID_SIZEgrid更合适。

这是一个好主意鼓励一些评论,尤其是对于那些神秘的代码。特别地,文档字符串是一个重要的习惯。


建议的实现

while True:
    …
    moves = moves + 1


评论


\ $ \ begingroup \ $
绝对是最易读的答案。我唯一的抱怨是使用_xy而不是_position或_pos,但是大多数人会认为一六个中的六个。
\ $ \ endgroup \ $
–法老王
16-2-27在1:46

\ $ \ begingroup \ $
@Pharap我已将_xy用作元组内容的助记符,因为元组可能是神秘的。我通常不喜欢这种后缀。如果我是为自己编写此代码,则将全部删除后缀,并使用其他语言功能(例如namedtuple或函数)使代码自记录。
\ $ \ endgroup \ $
– 200_success
16-2-27在2:38

\ $ \ begingroup \ $
@ 200_success为什么从这里阅读和理解起来容易得多,不使用*运算符会更简单?
\ $ \ endgroup \ $
–user98974
16年2月29日在14:01

\ $ \ begingroup \ $
非常好的答案。一根nitpick,文档字符串不应该在导入之前吗?
\ $ \ endgroup \ $
– magu_
16年4月26日在15:30

#3 楼

遵循的指南列表


将“组件”分解为类
对于非基于UI的游戏,我仍然应该认为它就像一个ui ,因此从绘制游戏板中打破游戏逻辑
避免嵌套条件
使您的python程序作为模块和可执行文件工作(如果使用if __name__ == '__main__',则在使用解释器时可以导入模块弄乱特定的组件并测试变量调整
在命名函数中使用verbs
为变量全名命名,不要懒惰,代码应像书一样阅读并具有自记录性
从性能的角度来看,您应该避免使用连续的打印语句,例如在印刷电路板时,选择改建字符串并使用一个打印语句转储到控制台中,这对教学来说不是什么大问题,但是最好的做法是写入文件或打印到控制台以限制这些交互
避免q4312079 q例如,您可以看到我为棋盘,玩家,龙和杀死/死亡定义了一个标记
坚持一致的变量名(不要使用loc_x和location_x,要坚持一个或另一个)
将动作移动到字典中可以使我摆脱大量的if / elif语句,并使代码更易于阅读
很多个人意见-逐行文档说明编码不好,其中变量和/或函数的命名不正确-您应该能够将注释限制为作为函数docstring的简短描述-我发现代码更易于阅读,而不必跳过其他所有注释行。
如果您确实不得不对条件块进行内联注释,我建议始终在上面的块内进行注释,这样可以防止出现剪切和粘贴错误,以免留下注释,现在将其留在代码中,描述错误的事情

例如:带有#号的所有内容我都认为不需要

def update_location(self, loc_x, loc_y):
    """Updates the players x and y coordinates, verifies they are not out of bounds prior to updating"""
    if self.range check(loc_x, loc_y):
        #  we have succeeded on the range check
        #  set the x-coordinate
        self.loc_x = loc_x
        #  set the y-coordinate
        self.loc_y = loc_y


鉴于以上列表,您可能还会发现一些可以在重构代码中进行改进的区域。一种方法是删除这些减法magic numbers and text,然后在Game self.grid_size-1:函数中定义__init__,以便在我们重新编写代码时可以理解比较-我非常不喜欢在布尔运算中进行数学运算。它使调试更加困难,并降低了代码的清晰度。随意更改重构代码

重构代码:

import random

class Grid(object):
  def __init__(self, size = 5, board_marker = '#', player_marker = 'X', dragon_marker = 'D'):
    self.size             = size
    self.board_marker     = board_marker
    self.player_marker    = player_marker
    self.dragon_marker    = dragon_marker
    self.death_marker     = 'K'

  def update_grid(self, player, dragon):
    dragon_marker = self.death_marker if player.loc_x == dragon.loc_x and player.loc_y == dragon.loc_y else self.dragon_marker
    grid          = [ x[:] for x in [[self.board_marker]*self.size] * self.size ]
    grid[player.loc_y][player.loc_x]  = self.player_marker
    grid[dragon.loc_y][dragon.loc_x]  = dragon_marker
    return "\n%s\n" % '\n'.join([' '.join(row) for row in grid ])

class Player(object):
  def __init__(self, loc_x = 0, loc_y = 0):
    self.loc_x = loc_x
    self.loc_y = loc_y

class Treasure(object):
  def __init__(self, grid_size = 5):
    self.loc_x = random.randint(0,(grid_size-1))
    self.loc_y = random.randint(0,(grid_size-1))

class Dragon(object):
  def __init__(self, grid_size = 5):
    self.loc_x = random.randint(0,(grid_size-1))
    self.loc_y = random.randint(0,(grid_size-1))
    self.grid_size = grid_size

  def update_location(self):
    self.loc_x = random.randint(0,(self.grid_size-1))
    self.loc_y = random.randint(0,(self.grid_size-1))

class Game(object):
  def __init__(self, grid_size = 5):
    self.moves      = 0
    self.grid_size  = grid_size
    self.player     = Player()
    self.treasure   = Treasure()
    self.grid       = Grid()
    self.dragon     = Dragon()
    self.directions = []
    self.movement   = {
                        'n':( 0, -1),
                        's':( 0,  1),
                        'e':( 1,  0),
                        'w':(-1,  0),
                      }

  def run(self):
    while True:
      print "Dragon wanders about..."
      self.dragon.update_location()
      print self.grid.update_grid(self.player, self.dragon)

      if self.check_dragon():
        print "You've been eaten by the dragon in %d moves" % self.moves
        break

      self.move_player()
      print self.grid.update_grid(self.player, self.dragon)
      if self.check_dragon():
        print "Well that was dumb, you walked right into the dragon in %d moves" % self.moves
        break
      if self.check_treasure():
        print "You found the treasure in %d moves" % self.moves
        break

  def move_player(self):
    self.update_available_directions()
    while True:
      m = raw_input('Which way would you like to move? [ %s ]' % ', '.join(self.directions)).lower()
      if m in self.directions:
        break
      else:
        print "Invalid movement direction"
    pdx, pdy            = self.movement[m]
    self.player.loc_x  += pdx
    self.player.loc_y  += pdy
    self.moves         += 1

  def update_available_directions(self):
    self.directions = []
    if self.player.loc_x > 0:
      self.directions.append('w')
    if self.player.loc_x < self.grid_size-1:
      self.directions.append('e')
    if self.player.loc_y > 0:
      self.directions.append('n')
    if self.player.loc_y < self.grid_size-1:
      self.directions.append('s')

  def check_dragon(self):
    return self.player.loc_x == self.dragon.loc_x and self.player.loc_y == self.dragon.loc_y

  def check_treasure(self):
    return self.player.loc_x == self.treasure.loc_x and self.player.loc_y == self.treasure.loc_y

if __name__ == "__main__":
  g = Game()
  g.run()


**我知道我的LR间隔排列为'='与PEP-8矛盾,但是我不这样做就无法阅读代码(轻度阅读障碍)。但是,这样做也被证明是有益的,并且可以轻松发现字典查询中的错误并查看错别字/复制粘贴错误。

评论


\ $ \ begingroup \ $
虽然我同意所有这些答案,但它也比原始代码复杂得多。如果向新手介绍这作为老师的解决方案,他们将有很多收获。
\ $ \ endgroup \ $
–致癌物质
16年2月28日在16:51

\ $ \ begingroup \ $
因此,@ Carcigenicate,我曾指导五年级学生编程,他们在理解对象方面做得很好。您可以说一个物体是一个苹果,它的重量,颜色,大小等。实际上,我对它们比其他物体更好的理解感到惊讶。在逻辑方面,将内容分解为单一职责功能实际上可以帮助他们了解如何构建程序。我问我的学生,ttt的规则是什么,胜利条件是什么,等等,当它们生成更多代码时,您可以通过问尖锐的问题来引导学生生成该代码。
\ $ \ endgroup \ $
– pyInTheSky
16-2-29在15:12

\ $ \ begingroup \ $
每场比赛我们都突破了核心概念/功能,并成为一项功能,它是否在确定举棋是否获胜,要求球员姓名,选择谁先参加比赛等。然后我们为每个功能加线可以回去说这5行代码可以做到这一点,依此类推... vs大量的代码。
\ $ \ endgroup \ $
– pyInTheSky
16年2月29日在15:14