פיתרון Advent Of Code 2024 יום 4 בשפת Ruby

05/02/2025

את התרגיל הזה קלוד לא הצליח לפתור, לפחות לא עם הפרומפט הפשוט שהדבקתי לו, לכן כבר הרגשתי שמח כשפתרתי אותו לבד. בסיפור שלנו היום אנחנו מקבלים קלט בצורה של מטריצה עם כל מיני אותיות למשל:

..X...
.SAMX.
.A..A.
XMAS.S
.X....

בחלק הראשון עלינו למצוא את כל המופעים של המילה XMAS בקלט אבל בכל הכיוונים (כולל אלכסונים והפוך), כמו בתפזורת מילים. בחלק השני צריך לחפש רק מופעים של MAS שמגיעים בצורת X כלומר:

M.S
.A.
M.S

וגם הם יכולים להיות הפוכים.

התרגיל המקורי נמצא בעמוד שלהם כאן: https://adventofcode.com/2024/day/4

תוכן עניינים

  1. פיתרון חלק 1
  2. חלק 2

1. פיתרון חלק 1

בשביל שני החלקים היה לי נוח לשמור את הקלט במילון, כשהמפתח הוא אינדקס והערך הוא התו שנמצא באינדקס הזה. בתרגילים של AoC למדתי שיותר קל לעבוד עם מילונים כאלה מאשר עם מערכים דו מימדיים. זה הקוד שיוצר את המילון ברובי:

$matrix = File.read('input.txt').lines.each_with_index.flat_map do |line, line_index|
  line.split('').each_with_index.map do |char, column_index|
    {[line_index, column_index] => char }
  end
end.reduce({}) {|acc, val| acc.merge(val) }

קצת ארוך אבל עובד. אם יש לכם רעיונות יותר קצרים אפשר לשתף בתגובות.

הפונקציה הבאה שכתבתי היתה go שפשוט מתקדמת מספר צעדים בכיוון מסוים. הכיוון הכללי לפיתרון היה לרוץ על כל האינדקסים במילון, מכל אינדקס להתקדם 4 צעדים לכל 8 הכיוונים ולראות אם כתובה שם המילה XMAS. הפונקציה go נראית כך:

def go(start, direction, steps = 4)
  Enumerator.produce(start) { |r, c| [r + direction[0], c + direction[1]] }.take(steps)
end

והפונקציה המרכזית של הפיתרון היא count_words_around שלוקחת נקודה ומריצה את go לכל 8 הכיוונים ואז סופרת כמה מהכיוונים האלה מכילים את המילה XMAS:

def count_words_around(point)
  indexes = [[0, 1],
             [1, 1],
             [1, 0],
             [1, -1],
             [0, -1],
             [-1, -1],
             [-1, 0],
             [-1, 1]].map { |direction| go(point, direction, 4) }
  indexes.count { |path| $matrix.slice(*path).values.join('') == 'XMAS' }
end

pp $matrix.keys.map {|point| count_words_around(point) }.sum

2. חלק 2

בחלק השני יצרתי מטריצה באותו אופן אבל על הפונקציה go כבר וויתרתי. במקום ללכת 4 צעדים מספיק לקחת את ה X שמסביב לנקודה ולבדוק אם כתוב בו MAS או SAM. זה הקוד:

def count_words_around(point)
  return 0 if $matrix[point] != 'A'

  around = [
    [-1, -1],
    [0, 0],
    [1, 1],
    [-1, 1],
    [0, 0],
    [1, -1]
  ].map { |direction| [point[0] + direction[0], point[1] + direction[1]] }
    .map {|i| $matrix[i] }.join('')

  if %w[MASMAS MASSAM SAMMAS SAMSAM].include?(around)
    1
  else
    0
  end
end

pp $matrix.keys.map {|point| count_words_around(point) }.sum