Решение на Втора задача от Бойко Караджов

Обратно към всички решения

Към профила на Бойко Караджов

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 20 успешни тест(а)
  • 0 неуспешни тест(а)

Код

def head_of(snake)
snake.last
end
def next_head(snake, direction)
(head_of snake).zip(direction).map { |s, d| s + d }
end
def all_positions(width, height)
Array.new(width * height) { |e| [e % width, e / width] }
end
def bites_self(snake, head)
snake.include? head
end
def hits_wall(head, dimensions)
x, y = head
x < 0 or x >= dimensions[:width] or y < 0 or y >= dimensions[:height]
end
def grow(snake, direction)
new_snake = snake.dup
new_snake << next_head(snake, direction)
end
def move(snake, direction)
new_snake = grow snake, direction
new_snake.shift
new_snake
end
def new_food(food, snake, dimensions)
forbidden = food | snake
positions = all_positions(dimensions[:width], dimensions[:height]) - forbidden
positions[rand positions.count]
end
def obstacle_ahead?(snake, direction, dimensions)
head = next_head snake, direction
bites_self snake, head or hits_wall head, dimensions
end
def danger?(snake, direction, dimensions)
if obstacle_ahead? snake, direction, dimensions
true
else
future_snake = move snake, direction
obstacle_ahead? future_snake, direction, dimensions
end
end

Лог от изпълнението

....................

Finished in 0.01492 seconds
20 examples, 0 failures

История (3 версии и 4 коментара)

Бойко обнови решението на 15.10.2015 23:01 (преди над 9 години)

+def next_head(snake, direction)
+ [snake.last[0] + direction[0], snake.last[1] + direction[1]]
+end
+
+def move(snake, direction)
+ new_snake = snake.dup
+ new_snake.shift
+ new_snake << next_head(snake, direction)
+end
+
+def grow(snake, direction)
+ new_snake = snake.dup
+ new_snake << next_head(snake, direction)
+end
+
+def new_food(food, snake, dimensions)
+ all_positions = *(0.. dimensions[:width] * dimensions[:height] - 1)
+ forbidden = (food | snake).map! { |p| p[0] + p[1] * dimensions[:width] }
+ positions = all_positions - forbidden
+ new_food_token = positions[rand positions.count]
+ [new_food_token % dimensions[:width], new_food_token / dimensions[:width] ]
+end
+
+def obstacle_ahead?(snake, direction, dimensions)
+ x, y = next_head snake, direction
+ x < 0 or x >= dimensions[:width] or y < 0 or y >= dimensions[:height]
+end
+
+def danger?(snake, direction, dimensions)
+ future_snake = move snake, direction
+ obstacle_ahead? future_snake, direction, dimensions
+end

Привет.

  • Фунциите obstacle_ahead? и danger? изпускат някои случаи. Погледни си отново условията за тях.

  • Функцията new_food прави твърде много неща :

    • Генерира храна на свободна позиция от дъската.
    • Знае как да кодира и декодира двумерни координати към едномерни.

    Добре е да пишем функции, които правят едно нещо и го правят много добре. Готиното при тези функции е, че тъй като правят само едно нещо ни е по-лесно да ги напишем изразително, без да има разни странични неща, които да ни разсейват от основната идея на функцията.

    В твоя случай, за да ти е удобно да генерираш всички позиции от дъската, използвайки Range, се налага да кодираш позициите на храната и змията, а след това да декодираш новата позиция на храната. Ако това кодиране е напълно наложащо, аз бих го извадил в отделни фукции, за да не добавя такава сложност към generate_food. За конкретната задача обаче има по-добре решение. Погледни документацията на Array, там има един метод, който е доста удобен за генерирането на всички позиции от дъската.

  • Не се притеснявай да пишеш помощни функции, които да ти правят кода по-четим. Например за да достъпиш главата на змията викаш snake.last, можеш да напишеш функция snake_head.

Бойко обнови решението на 18.10.2015 05:32 (преди над 9 години)

+def head_of(snake)
+ snake.last
+end
+
def next_head(snake, direction)
- [snake.last[0] + direction[0], snake.last[1] + direction[1]]
+ (head_of snake).zip(direction).map { |s, d| s + d }
end
-def move(snake, direction)
- new_snake = snake.dup
- new_snake.shift
- new_snake << next_head(snake, direction)
+def matrix(width, height)
+ Array.new(width * height) { |e| [e % width, e / width] }
end
def grow(snake, direction)
new_snake = snake.dup
new_snake << next_head(snake, direction)
end
+def move(snake, direction)
+ new_snake = grow snake, direction
+ new_snake.shift
+ new_snake
+end
+
def new_food(food, snake, dimensions)
- all_positions = *(0.. dimensions[:width] * dimensions[:height] - 1)
- forbidden = (food | snake).map! { |p| p[0] + p[1] * dimensions[:width] }
+ all_positions = matrix dimensions[:width], dimensions[:height]
+ forbidden = food | snake
positions = all_positions - forbidden
- new_food_token = positions[rand positions.count]
- [new_food_token % dimensions[:width], new_food_token / dimensions[:width] ]
+ positions[rand positions.count]
end
def obstacle_ahead?(snake, direction, dimensions)
x, y = next_head snake, direction
- x < 0 or x >= dimensions[:width] or y < 0 or y >= dimensions[:height]
+ width = dimensions[:width]
+ height = dimensions[:height]
+ bites_self = snake.include? [x, y]
+ bites_self or x < 0 or x >= width or y < 0 or y >= height
end
def danger?(snake, direction, dimensions)
- future_snake = move snake, direction
- obstacle_ahead? future_snake, direction, dimensions
+ if obstacle_ahead? snake, direction, dimensions then
+ true
+ else
+ future_snake = move snake, direction
+ obstacle_ahead? future_snake, direction, dimensions
+ end
end

Много си бърз :)

  • new_food вече е straight to the point, четеш и ти става ясно какво се случва. Обаче, ако я нямаше променливата all_positions, нямаше да разбера какво връща matrix. Мисленето на имена си е трудна работа, но колкото е трудна, толкова е и важна :). Конкретно тази matrix, аз бих я кръстил all_positions, или board_positions. Ако я кръстиш all_positions и подаваш dimensions, може би няма да имаш нужда от временна променлива в която да записваш резултата.

  • В obstacle_ahead? имаш едни променливи, width и height, всяка от които използваш само на едно място и имената им не ти носят нова информация за съдържанието. width не ти казва нещо повече от dimensions[:width]. Те са безсмислени. Променливата bites_self също се използва на едно място, но пък името и ти дава доста добра информация какво проверява snake.include? [x, y], тъй че тя е супер. Предполагам си направил width и height, защото иначе 36-ти ред ще стане повече от 80 символа. Това което ти предлагам да направиш е да изведеш проверките за това дали се захапва змията и дали излиза от дъската в отделни функции, които да викаш в obstacle_ahead?.

  • Пиши then само, когато ползваш if на един ред, ето така:

    if sleepy? then sleep(8.hours) else code(2.hours) end
    

    В останалите случаи го изпускай. Това го има описано в style guide-a.

    Всъщност.. опитай се да разкараш if-a от danger?. Не ти е нужен. Там имаш само едно булево условие - змията е в опасност, ако умира-след-един-ход или умира-след-два-хода.

Бойко обнови решението на 18.10.2015 22:09 (преди над 9 години)

def head_of(snake)
snake.last
end
def next_head(snake, direction)
(head_of snake).zip(direction).map { |s, d| s + d }
end
-def matrix(width, height)
+def all_positions(width, height)
Array.new(width * height) { |e| [e % width, e / width] }
end
+def bites_self(snake, head)
+ snake.include? head
+end
+
+def hits_wall(head, dimensions)
+ x, y = head
+ x < 0 or x >= dimensions[:width] or y < 0 or y >= dimensions[:height]
+end
+
def grow(snake, direction)
new_snake = snake.dup
new_snake << next_head(snake, direction)
end
def move(snake, direction)
new_snake = grow snake, direction
new_snake.shift
new_snake
end
def new_food(food, snake, dimensions)
- all_positions = matrix dimensions[:width], dimensions[:height]
forbidden = food | snake
- positions = all_positions - forbidden
+ positions = all_positions(dimensions[:width], dimensions[:height]) - forbidden
positions[rand positions.count]
end
def obstacle_ahead?(snake, direction, dimensions)
- x, y = next_head snake, direction
- width = dimensions[:width]
- height = dimensions[:height]
- bites_self = snake.include? [x, y]
- bites_self or x < 0 or x >= width or y < 0 or y >= height
+ head = next_head snake, direction
+ bites_self snake, head or hits_wall head, dimensions
end
def danger?(snake, direction, dimensions)
- if obstacle_ahead? snake, direction, dimensions then
+ if obstacle_ahead? snake, direction, dimensions
true
else
future_snake = move snake, direction
obstacle_ahead? future_snake, direction, dimensions
end
end