Решение на Втора задача от Ивайло Христов

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

Към профила на Ивайло Христов

Резултати

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

Код

def move(snake, direction)
snake[1..-1].push(next_position(snake, direction))
end
def grow(snake, direction)
snake[0..-1].push(next_position(snake, direction))
end
def new_food(food, snake, dimensions)
positions = generate_playground(dimensions[:width], dimensions[:height])
positions.select { |position| position_free?(position, food + snake) }.sample
end
def obstacle_ahead?(snake, *directions, dimensions)
directions.any? do |direction|
head = next_position(snake, direction)
blocked = outside?(head, dimensions.values) || !position_free?(head, snake)
snake = move(snake, direction)
blocked
end
end
def danger?(snake, direction, dimensions)
obstacle_ahead?(snake, direction, direction, dimensions)
end
def next_position(snake, direction)
# snake.last.map.with_index { |axis, index| axis + direction[index] }
[snake.last, direction].transpose.map { |position| position.reduce(:+) }
end
def generate_playground(width, height)
[*0..(width - 1)].product([*0..(height - 1)])
end
def outside?(position, dimensions)
outside_condition = ->(axis, index) { axis >= dimensions[index] or axis < 0 }
position.find.with_index(&outside_condition) != nil
end
def position_free?(position, occupied_positions)
!occupied_positions.include?(position)
end

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

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

Finished in 0.01489 seconds
20 examples, 0 failures

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

Ивайло обнови решението на 13.10.2015 13:02 (преди над 9 години)

+def move(snake, direction)
+ snake[1 .. -1].push(snake.last.map.with_index { |e, i| e + direction[i] })
+end
+
+def grow(snake, direction)
+ snake[0 .. -1].push(snake.last.map.with_index { |e, i| e + direction[i] })
+end
+
+def new_food(food, snake, dimensions)
+ positions = [* 0 .. dimensions.values.max - 1].repeated_permutation(2).to_a
+ free_positions = positions.select { |e| !(food + snake).include?(e) }
+
+ free_positions.sample
+end
+
+def obstacle_ahead?(snake, direction, dimensions)
+ new_position = snake.last.map.with_index { |e, i| e + direction[i] }
+
+ up_limit = [new_position.first, dimensions.values.last]
+ right_limit = [dimensions.values.first, new_position.last]
+ direction_obstacle = [up_limit, right_limit]
+ obstacles = [0, -1].permutation.to_a + snake + direction_obstacle
+
+ obstacles.include?(new_position)
+end
+
+def danger?(snake, direction, dimensions)
+ new_position = [snake.last.map.with_index { |e, i| e + direction[i] }]
+
+ obstacle_next = obstacle_ahead?(snake, direction, dimensions)
+ obstacle_after = obstacle_ahead?(snake + new_position, direction, dimensions)
+
+ obstacle_next or obstacle_after
+end

Ивайло обнови решението на 13.10.2015 19:40 (преди над 9 години)

def move(snake, direction)
snake[1 .. -1].push(snake.last.map.with_index { |e, i| e + direction[i] })
end
def grow(snake, direction)
- snake[0 .. -1].push(snake.last.map.with_index { |e, i| e + direction[i] })
+ snake.dup.push(snake.last.map.with_index { |e, i| e + direction[i] })
end
def new_food(food, snake, dimensions)
- positions = [* 0 .. dimensions.values.max - 1].repeated_permutation(2).to_a
+ x_positions = [* 0 .. (dimensions[:width] - 1)]
+ y_positions = [* 0 .. (dimensions[:height] - 1)]
+
+ positions = y_positions.map{ |e| x_positions.zip([e] * x_positions.size) }
+ positions.flatten!(1)
free_positions = positions.select { |e| !(food + snake).include?(e) }
free_positions.sample
end
def obstacle_ahead?(snake, direction, dimensions)
new_position = snake.last.map.with_index { |e, i| e + direction[i] }
up_limit = [new_position.first, dimensions.values.last]
right_limit = [dimensions.values.first, new_position.last]
direction_obstacle = [up_limit, right_limit]
+
obstacles = [0, -1].permutation.to_a + snake + direction_obstacle
obstacles.include?(new_position)
end
def danger?(snake, direction, dimensions)
new_position = [snake.last.map.with_index { |e, i| e + direction[i] }]
obstacle_next = obstacle_ahead?(snake, direction, dimensions)
obstacle_after = obstacle_ahead?(snake + new_position, direction, dimensions)
obstacle_next or obstacle_after
end

btw skeptic иска разстояния в range-овете. Само на мен ли ми изглежда екстремно грозно? :D

Не. Добра забележка, skeptic има нова версия, след gem install skeptic би трябвало да ти се update-не. С нея ще можеш да ползваш ..(.), [*..] без спейсове около тях.

Надявам се сайтът да се update-не днес или утре с нея, за да може тук да submit-ваш нормалната версия

EDIT: Сайтът вече е обновен, така че вече и тук ще се валидира.

Ивайло обнови решението на 14.10.2015 11:07 (преди над 9 години)

def move(snake, direction)
- snake[1 .. -1].push(snake.last.map.with_index { |e, i| e + direction[i] })
+ snake[1..-1].push(snake.last.map.with_index { |e, i| e + direction[i] })
end
def grow(snake, direction)
snake.dup.push(snake.last.map.with_index { |e, i| e + direction[i] })
end
def new_food(food, snake, dimensions)
- x_positions = [* 0 .. (dimensions[:width] - 1)]
- y_positions = [* 0 .. (dimensions[:height] - 1)]
+ x_positions = [*0..(dimensions[:width] - 1)]
+ y_positions = [*0..(dimensions[:height] - 1)]
positions = y_positions.map{ |e| x_positions.zip([e] * x_positions.size) }
positions.flatten!(1)
free_positions = positions.select { |e| !(food + snake).include?(e) }
free_positions.sample
end
def obstacle_ahead?(snake, direction, dimensions)
new_position = snake.last.map.with_index { |e, i| e + direction[i] }
- up_limit = [new_position.first, dimensions.values.last]
- right_limit = [dimensions.values.first, new_position.last]
- direction_obstacle = [up_limit, right_limit]
+ outside = outside?(new_position, [dimensions[:width], dimensions[:height]])
- obstacles = [0, -1].permutation.to_a + snake + direction_obstacle
-
- obstacles.include?(new_position)
+ outside or snake.include?(new_position)
end
def danger?(snake, direction, dimensions)
new_position = [snake.last.map.with_index { |e, i| e + direction[i] }]
obstacle_next = obstacle_ahead?(snake, direction, dimensions)
obstacle_after = obstacle_ahead?(snake + new_position, direction, dimensions)
obstacle_next or obstacle_after
+end
+
+def outside?(position, dimensions)
+ position.find.with_index { |e, i| e >= dimensions[i] or e < 0 } != nil
end

Малко бележки:

  • Защо в grow използваш dup, а в move - snake[1..-1]? Избери едно и бъди консистентен.
  • DRY! move, grow и obstacle_ahead? имат нещо общо. Изкарай го в метод и му дай подходящо име.
  • На 13-ти ред си пропуснал един интервал между map-a и блока.
  • new_food може да се напише по-добре. Помъчи се малко и измисли по-елегантна имплементация (: Ако не успееш, пиши тук за насоки.
  • На 23-ти ред имаш WTF момент. Виж дали няма по-удачен начин да вземеш всички стойности (без ключове, само стойностите им) на хеш като масив. Hint: Чети документацията на Hash.
  • Какъв е смисълът от тази междинна променлива, outside, в obstacle_ahead? Според мен е ненужна.
  • e, i... Внимавай! С тези имена на променливи си просиш наказателни точки! Да им дадеш подходящи имена.
  • Направил си метод outside?. Хубаво! Защо не си направил подобен метод и за snake.include?(new_position), който също да описва идеята на тази проверка?
  • new_food и danger? могат да се доизпипат. Помисли върху тях.

Има какво да подобриш, тъй че действай.

Ивайло обнови решението на 15.10.2015 12:03 (преди над 9 години)

+require 'Matrix'
+
def move(snake, direction)
- snake[1..-1].push(snake.last.map.with_index { |e, i| e + direction[i] })
+ snake[1..-1].push(next_position(snake, direction))
end
def grow(snake, direction)
- snake.dup.push(snake.last.map.with_index { |e, i| e + direction[i] })
+ snake[0..-1].push(next_position(snake, direction))
end
def new_food(food, snake, dimensions)
- x_positions = [*0..(dimensions[:width] - 1)]
- y_positions = [*0..(dimensions[:height] - 1)]
+ positions = generate_matrix(dimensions[:width], dimensions[:height])
- positions = y_positions.map{ |e| x_positions.zip([e] * x_positions.size) }
- positions.flatten!(1)
- free_positions = positions.select { |e| !(food + snake).include?(e) }
-
- free_positions.sample
+ positions.select { |position| position_free?(food + snake, position) }.sample
end
def obstacle_ahead?(snake, direction, dimensions)
- new_position = snake.last.map.with_index { |e, i| e + direction[i] }
+ next_position = next_position(snake, direction)
- outside = outside?(new_position, [dimensions[:width], dimensions[:height]])
+ outside = outside?(next_position, dimensions.values)
- outside or snake.include?(new_position)
+ outside or ! position_free?(snake, next_position)
end
def danger?(snake, direction, dimensions)
- new_position = [snake.last.map.with_index { |e, i| e + direction[i] }]
+ next_position = [next_position(snake, direction)]
obstacle_next = obstacle_ahead?(snake, direction, dimensions)
- obstacle_after = obstacle_ahead?(snake + new_position, direction, dimensions)
+ obstacle_after = obstacle_ahead?(snake + next_position, direction, dimensions)
obstacle_next or obstacle_after
end
+def next_position(snake, direction)
+ snake.last.map.with_index { |axis, index| axis + direction[index] }
+end
+
+def generate_matrix(width, height)
+ Matrix.build(width, height) { |row, col| [row, col] }.to_a.flatten(1)
+end
+
def outside?(position, dimensions)
- position.find.with_index { |e, i| e >= dimensions[i] or e < 0 } != nil
+ outside_condition = ->(axis, index) { axis >= dimensions[index] or axis < 0 }
+
+ position.find.with_index(&outside_condition) != nil
+end
+
+def position_free?(occupied_positions, position)
+ ! occupied_positions.include?(position)
end

Позволено ли е използването на допълнителни библиотеки от Ruby (Matrix в случая)?
generate_matrix метода първоначално го написах така:
Array.new(width) { |row| Array.new(height) { |col| [row, col] } }
Но така нарушава правилото за влагане и не можах да го измисля по друг начин без да нарушавам друго стилово правило.

Колкото до забележките:

  • OK
  • OK
  • Whoops
  • Промених го, но не знам дали това си имал предвид
  • Знам за Hash#values, но забравих, че стойностите винаги ще са валидни (:width, :height, а не :height, :width) и това ми беше проверката
  • Без нея надминавам ограничението за 80 символа на ред :D
  • ОК
  • Реших, че е лоша идея да правя метод, който не извършва допълнително нещо (откъм функционалност)
  • За danger? нещо друго освен new_position трябва ли да оправя?

Also another skeptc thing: изисква ми space около ! (! position_free?)

EDIT: Idea! obstacle_ahead? да приема n на брой directions и да проверява всяка стъпка. По този начин danger? може да се направи по-яко. Good idea?

Ивайло обнови решението на 15.10.2015 19:50 (преди над 9 години)

require 'Matrix'
def move(snake, direction)
snake[1..-1].push(next_position(snake, direction))
end
def grow(snake, direction)
snake[0..-1].push(next_position(snake, direction))
end
def new_food(food, snake, dimensions)
- positions = generate_matrix(dimensions[:width], dimensions[:height])
+ positions = generate_playground(dimensions[:width], dimensions[:height])
positions.select { |position| position_free?(food + snake, position) }.sample
end
-def obstacle_ahead?(snake, direction, dimensions)
- next_position = next_position(snake, direction)
+def obstacle_ahead?(snake, *directions, dimensions)
+ directions.any? do |direction|
+ snake = move(snake, direction)
+ head = snake.last
- outside = outside?(next_position, dimensions.values)
-
- outside or ! position_free?(snake, next_position)
+ outside?(head, dimensions.values) or ! position_free?(snake[0.. - 2], head)
+ end
end
def danger?(snake, direction, dimensions)
- next_position = [next_position(snake, direction)]
-
- obstacle_next = obstacle_ahead?(snake, direction, dimensions)
- obstacle_after = obstacle_ahead?(snake + next_position, direction, dimensions)
-
- obstacle_next or obstacle_after
+ obstacle_ahead?(snake, direction, direction, dimensions)
end
def next_position(snake, direction)
snake.last.map.with_index { |axis, index| axis + direction[index] }
end
-def generate_matrix(width, height)
+def generate_playground(width, height)
Matrix.build(width, height) { |row, col| [row, col] }.to_a.flatten(1)
end
def outside?(position, dimensions)
outside_condition = ->(axis, index) { axis >= dimensions[index] or axis < 0 }
position.find.with_index(&outside_condition) != nil
end
def position_free?(occupied_positions, position)
! occupied_positions.include?(position)
end

Ивайло обнови решението на 15.10.2015 21:40 (преди над 9 години)

require 'Matrix'
def move(snake, direction)
snake[1..-1].push(next_position(snake, direction))
end
def grow(snake, direction)
snake[0..-1].push(next_position(snake, direction))
end
def new_food(food, snake, dimensions)
positions = generate_playground(dimensions[:width], dimensions[:height])
- positions.select { |position| position_free?(food + snake, position) }.sample
+ positions.select { |position| position_free?(position, food + snake) }.sample
end
def obstacle_ahead?(snake, *directions, dimensions)
directions.any? do |direction|
snake = move(snake, direction)
head = snake.last
- outside?(head, dimensions.values) or ! position_free?(snake[0.. - 2], head)
+ outside?(head, dimensions.values) or !position_free?(head, snake[0..-2])
end
end
def danger?(snake, direction, dimensions)
obstacle_ahead?(snake, direction, direction, dimensions)
end
def next_position(snake, direction)
snake.last.map.with_index { |axis, index| axis + direction[index] }
end
def generate_playground(width, height)
Matrix.build(width, height) { |row, col| [row, col] }.to_a.flatten(1)
end
def outside?(position, dimensions)
outside_condition = ->(axis, index) { axis >= dimensions[index] or axis < 0 }
position.find.with_index(&outside_condition) != nil
end
-def position_free?(occupied_positions, position)
- ! occupied_positions.include?(position)
+def position_free?(position, occupied_positions)
+ !occupied_positions.include?(position)
end

Ивайло обнови решението на 16.10.2015 01:17 (преди над 9 години)

-require 'Matrix'
-
def move(snake, direction)
snake[1..-1].push(next_position(snake, direction))
end
def grow(snake, direction)
snake[0..-1].push(next_position(snake, direction))
end
def new_food(food, snake, dimensions)
positions = generate_playground(dimensions[:width], dimensions[:height])
positions.select { |position| position_free?(position, food + snake) }.sample
end
def obstacle_ahead?(snake, *directions, dimensions)
directions.any? do |direction|
snake = move(snake, direction)
head = snake.last
outside?(head, dimensions.values) or !position_free?(head, snake[0..-2])
end
end
def danger?(snake, direction, dimensions)
obstacle_ahead?(snake, direction, direction, dimensions)
end
def next_position(snake, direction)
snake.last.map.with_index { |axis, index| axis + direction[index] }
end
def generate_playground(width, height)
- Matrix.build(width, height) { |row, col| [row, col] }.to_a.flatten(1)
+ [*0..(width - 1)].product([*0..(height - 1)])
end
def outside?(position, dimensions)
outside_condition = ->(axis, index) { axis >= dimensions[index] or axis < 0 }
position.find.with_index(&outside_condition) != nil
end
def position_free?(position, occupied_positions)
!occupied_positions.include?(position)
end

Ивайло обнови решението на 16.10.2015 09:13 (преди над 9 години)

def move(snake, direction)
snake[1..-1].push(next_position(snake, direction))
end
def grow(snake, direction)
snake[0..-1].push(next_position(snake, direction))
end
def new_food(food, snake, dimensions)
positions = generate_playground(dimensions[:width], dimensions[:height])
positions.select { |position| position_free?(position, food + snake) }.sample
end
def obstacle_ahead?(snake, *directions, dimensions)
directions.any? do |direction|
+ head = next_position(snake, direction)
+ blocked = outside?(head, dimensions.values) || !position_free?(head, snake)
snake = move(snake, direction)
- head = snake.last
- outside?(head, dimensions.values) or !position_free?(head, snake[0..-2])
+ blocked
end
end
def danger?(snake, direction, dimensions)
obstacle_ahead?(snake, direction, direction, dimensions)
end
def next_position(snake, direction)
- snake.last.map.with_index { |axis, index| axis + direction[index] }
+ # snake.last.map.with_index { |axis, index| axis + direction[index] }
+ [snake.last, direction].transpose.map { |position| position.reduce(:+) }
end
def generate_playground(width, height)
[*0..(width - 1)].product([*0..(height - 1)])
end
def outside?(position, dimensions)
outside_condition = ->(axis, index) { axis >= dimensions[index] or axis < 0 }
position.find.with_index(&outside_condition) != nil
end
def position_free?(position, occupied_positions)
!occupied_positions.include?(position)
end
  • Променил съм начина, по който смятам next_position. Стария съм оставил като коментар. Which one is better?
  • Промених obstacle_ahead?, тъй като при змия с дължина 2 не работеше (първо се мърда и след това проверява дали позицията е валидна, сега е обратното), обаче сега май е грозно. Има ли начин как да направя проверка в блока на any?, която ако е true веднага да излиза от него, а ако не е да продължи И да не е поставена на края на блока или да нарушава някое от skeptic правилата?
    (damn you --max-nesting-depth 1)

EDIT: не знам на каква магия работи break true ама върши това, което искам. Sadly пак не мога да го ползвам (break true if condition също се брои за nesting :sob:)
Щеше да изглежда така:

def obstacle_ahead?(snake, *directions, dimensions)
  directions.any? do |direction|
    head = next_position(snake, direction)

    blocked = outside?(head, dimensions.values) || !position_free?(head, snake)
    break true if blocked

    snake = move(snake, direction) and next
  end
end