Compare commits
8 Commits
2239760ce2
...
main
Author | SHA1 | Date | |
---|---|---|---|
fe8e901212
|
|||
4a62be948b
|
|||
ac6082f993
|
|||
632868f7f3
|
|||
7326d17948
|
|||
a786262683
|
|||
e70e71b068
|
|||
c7e8bf103e
|
@ -24,7 +24,9 @@ let package = Package(
|
|||||||
.executable(name: "12", targets: ["12"]),
|
.executable(name: "12", targets: ["12"]),
|
||||||
.executable(name: "13", targets: ["13"]),
|
.executable(name: "13", targets: ["13"]),
|
||||||
.executable(name: "14", targets: ["14"]),
|
.executable(name: "14", targets: ["14"]),
|
||||||
.executable(name: "15", targets: ["15"])
|
.executable(name: "15", targets: ["15"]),
|
||||||
|
.executable(name: "16", targets: ["16"]),
|
||||||
|
.executable(name: "17", targets: ["17"])
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
// Dependencies declare other packages that this package depends on.
|
// Dependencies declare other packages that this package depends on.
|
||||||
@ -112,12 +114,20 @@ let package = Package(
|
|||||||
.executableTarget(
|
.executableTarget(
|
||||||
name: "14",
|
name: "14",
|
||||||
dependencies: [ .targetItem(name: "Runner", condition: nil) ]
|
dependencies: [ .targetItem(name: "Runner", condition: nil) ]
|
||||||
)
|
),
|
||||||
,
|
|
||||||
.executableTarget(
|
.executableTarget(
|
||||||
name: "15",
|
name: "15",
|
||||||
dependencies: [ .targetItem(name: "Runner", condition: nil),
|
dependencies: [ .targetItem(name: "Runner", condition: nil),
|
||||||
.product(name: "Collections", package: "swift-collections")]
|
.product(name: "Collections", package: "swift-collections")]
|
||||||
|
),
|
||||||
|
.executableTarget(
|
||||||
|
name: "16",
|
||||||
|
dependencies: [ .targetItem(name: "Runner", condition: nil) ]
|
||||||
|
),
|
||||||
|
.executableTarget(
|
||||||
|
name: "17",
|
||||||
|
dependencies: [ .targetItem(name: "Runner", condition: nil),
|
||||||
|
.product(name: "Collections", package: "swift-collections")]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -38,22 +38,11 @@ class Day15: Runnable {
|
|||||||
self.inputPath = inputPath
|
self.inputPath = inputPath
|
||||||
}
|
}
|
||||||
|
|
||||||
public func run() {
|
private func expandMap() {
|
||||||
let input = try! String(contentsOfFile: inputPath)
|
|
||||||
parts = input
|
|
||||||
.trimmingCharacters(in: .newlines)
|
|
||||||
.components(separatedBy: .newlines)
|
|
||||||
.map { Array($0) }
|
|
||||||
.map { ar in ar.map {c in ThreatLevel(c.description)!} }
|
|
||||||
lastRow = parts.count - 1
|
|
||||||
lastCol = (parts.first?.count ?? 0) - 1
|
|
||||||
|
|
||||||
var tmpParts = parts
|
var tmpParts = parts
|
||||||
for (rowNum, row) in tmpParts.enumerated() {
|
for (rowNum, row) in tmpParts.enumerated() {
|
||||||
let newRow = [Int](0...4).flatMap { cr in
|
let newRow = [Int](0...4).flatMap { cr in
|
||||||
row
|
row.map { rv in cr + rv }.map { $0 > 9 ? $0 - 9 : $0 }
|
||||||
.map { rv in cr + rv }
|
|
||||||
.map { $0 > 9 ? $0 - 9 : $0 }
|
|
||||||
}
|
}
|
||||||
parts[rowNum] = newRow
|
parts[rowNum] = newRow
|
||||||
}
|
}
|
||||||
@ -69,7 +58,25 @@ class Day15: Runnable {
|
|||||||
|
|
||||||
lastRow = parts.count - 1
|
lastRow = parts.count - 1
|
||||||
lastCol = (parts.first?.count ?? 0) - 1
|
lastCol = (parts.first?.count ?? 0) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public func run() {
|
||||||
|
let input = try! String(contentsOfFile: inputPath)
|
||||||
|
parts = input
|
||||||
|
.trimmingCharacters(in: .newlines)
|
||||||
|
.components(separatedBy: .newlines)
|
||||||
|
.map { Array($0) }
|
||||||
|
.map { ar in ar.map {c in ThreatLevel(c.description)!} }
|
||||||
|
lastRow = parts.count - 1
|
||||||
|
lastCol = (parts.first?.count ?? 0) - 1
|
||||||
|
|
||||||
|
calculateSafestPath()
|
||||||
|
expandMap()
|
||||||
|
neighbors = [Coord: [Coord]]()
|
||||||
|
calculateSafestPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func calculateSafestPath() {
|
||||||
// Distances to startNode
|
// Distances to startNode
|
||||||
var totalThreatLevel = [Coord: ThreatLevel]()
|
var totalThreatLevel = [Coord: ThreatLevel]()
|
||||||
|
|
||||||
@ -78,6 +85,13 @@ class Day15: Runnable {
|
|||||||
Coord(row: row.offset, col: col)
|
Coord(row: row.offset, col: col)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/* Keep a seperate dictionary, to calculate which coordinates have a set threat level
|
||||||
|
* a.k.a total distance to start
|
||||||
|
* This allows to find the next coordinate to check fairly quickly, by just
|
||||||
|
* looking at the minmum key in this.
|
||||||
|
* I initially tried a PriorityQueue, but my implementation was pretty slow and it spent too much time sorting
|
||||||
|
*/
|
||||||
var threatLevelDistrubution = [ThreatLevel: Set<Coord>]()
|
var threatLevelDistrubution = [ThreatLevel: Set<Coord>]()
|
||||||
let startNode = Coord(row: 0, col: 0)
|
let startNode = Coord(row: 0, col: 0)
|
||||||
totalThreatLevel[startNode] = 0
|
totalThreatLevel[startNode] = 0
|
||||||
@ -89,6 +103,8 @@ class Day15: Runnable {
|
|||||||
let currentNode = threatLevelDistrubution[threatLevel]!.first!
|
let currentNode = threatLevelDistrubution[threatLevel]!.first!
|
||||||
unvisited.remove(currentNode)
|
unvisited.remove(currentNode)
|
||||||
threatLevelDistrubution[threatLevel]!.remove(currentNode)
|
threatLevelDistrubution[threatLevel]!.remove(currentNode)
|
||||||
|
|
||||||
|
// Remove key from dictionary if not coords are left for a specific threat level
|
||||||
if threatLevelDistrubution[threatLevel]!.isEmpty {
|
if threatLevelDistrubution[threatLevel]!.isEmpty {
|
||||||
threatLevelDistrubution.removeValue(forKey: threatLevel)
|
threatLevelDistrubution.removeValue(forKey: threatLevel)
|
||||||
}
|
}
|
||||||
@ -114,6 +130,8 @@ class Day15: Runnable {
|
|||||||
totalThreatLevel[a] = testCost
|
totalThreatLevel[a] = testCost
|
||||||
threatLevelDistrubution[currentCost]!.remove(a)
|
threatLevelDistrubution[currentCost]!.remove(a)
|
||||||
threatLevelDistrubution[testCost, default: Set<Coord>()].insert(a)
|
threatLevelDistrubution[testCost, default: Set<Coord>()].insert(a)
|
||||||
|
|
||||||
|
// Remove key from dictionary if not coords are left for a specific threat leveli
|
||||||
if threatLevelDistrubution[currentCost]!.isEmpty {
|
if threatLevelDistrubution[currentCost]!.isEmpty {
|
||||||
threatLevelDistrubution.removeValue(forKey: currentCost)
|
threatLevelDistrubution.removeValue(forKey: currentCost)
|
||||||
}
|
}
|
||||||
|
191
Sources/16/16.swift
Normal file
191
Sources/16/16.swift
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
//
|
||||||
|
// File.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Max Nuding on 15.12.21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Runner
|
||||||
|
|
||||||
|
|
||||||
|
enum OperatorType: Int {
|
||||||
|
case sum = 0
|
||||||
|
case product = 1
|
||||||
|
case minimum = 2
|
||||||
|
case maximum = 3
|
||||||
|
case value = 4
|
||||||
|
case greaterThan = 5
|
||||||
|
case lessThan = 6
|
||||||
|
case equal = 7
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol Packet {
|
||||||
|
var version: Int { get }
|
||||||
|
|
||||||
|
func getTotalVersion() -> Int
|
||||||
|
func getValue() -> Int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValuePacket: Packet {
|
||||||
|
let version: Int
|
||||||
|
let value: Int
|
||||||
|
|
||||||
|
func getTotalVersion() -> Int { version }
|
||||||
|
func getValue() -> Int { value }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OperatorPacket: Packet {
|
||||||
|
let type: OperatorType
|
||||||
|
let version: Int
|
||||||
|
let subPackets: [Packet]
|
||||||
|
|
||||||
|
func getTotalVersion() -> Int { version + subPackets.map { $0.getTotalVersion() }.reduce(0, +) }
|
||||||
|
func getValue() -> Int {
|
||||||
|
let spValues = subPackets.map { $0.getValue() }
|
||||||
|
switch type {
|
||||||
|
case .sum:
|
||||||
|
return spValues.reduce(0, +)
|
||||||
|
case .product:
|
||||||
|
return spValues.reduce(1, *)
|
||||||
|
case .minimum:
|
||||||
|
return spValues.min()!
|
||||||
|
case .maximum:
|
||||||
|
return spValues.max()!
|
||||||
|
case .greaterThan:
|
||||||
|
return spValues.first! > spValues.last! ? 1 : 0
|
||||||
|
case .lessThan:
|
||||||
|
return spValues.first! < spValues.last! ? 1 : 0
|
||||||
|
case .equal:
|
||||||
|
return spValues.first! == spValues.last! ? 1 : 0
|
||||||
|
case .value:
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Day16: Runnable {
|
||||||
|
let inputPath: String
|
||||||
|
|
||||||
|
required init(inputPath: String) {
|
||||||
|
self.inputPath = inputPath
|
||||||
|
}
|
||||||
|
|
||||||
|
public func run() {
|
||||||
|
let input = try! String(contentsOfFile: inputPath)
|
||||||
|
let parts = input
|
||||||
|
.trimmingCharacters(in: .newlines)
|
||||||
|
let bytes = parts.bits().joined()
|
||||||
|
let packets = parsePacket(bytes: bytes)
|
||||||
|
let p: Packet = packets.0
|
||||||
|
print(p.getTotalVersion())
|
||||||
|
print(p.getValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePacket<S>(bytes: S) -> (Packet, S.SubSequence) where S:StringProtocol {
|
||||||
|
let version = bytes[bytes.startIndex..<bytes.index(bytes.startIndex, offsetBy: 3)].binaryToDecimal()
|
||||||
|
let typeId = bytes[bytes.index(bytes.startIndex, offsetBy: 3)..<bytes.index(bytes.startIndex, offsetBy: 6)].binaryToDecimal()
|
||||||
|
var value: Packet
|
||||||
|
var reminder: S.SubSequence
|
||||||
|
switch typeId {
|
||||||
|
case 4:
|
||||||
|
let (v, r) = parseLiteralPacketValue(bytes: bytes)
|
||||||
|
value = ValuePacket(version: version, value: v)
|
||||||
|
reminder = r
|
||||||
|
default:
|
||||||
|
let (v, r) = parseOperatorPacker(bytes: bytes)
|
||||||
|
value = OperatorPacket(type: OperatorType(rawValue: typeId)!, version: version, subPackets: v)
|
||||||
|
reminder = r
|
||||||
|
}
|
||||||
|
return (value, reminder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseOperatorPacker<S>(bytes: S) -> ([Packet], S.SubSequence) where S:StringProtocol {
|
||||||
|
let startNextBitsIndex = bytes.index(bytes.startIndex, offsetBy: 7)
|
||||||
|
let lengthTypeId = bytes[bytes.index(bytes.startIndex, offsetBy: 6)..<startNextBitsIndex].description
|
||||||
|
let isTotalLength = lengthTypeId == "0"
|
||||||
|
|
||||||
|
let nextBitsLength = isTotalLength ? 15 : 11
|
||||||
|
let endNextBitsIndex = bytes.index(startNextBitsIndex, offsetBy: nextBitsLength)
|
||||||
|
let nextBits = bytes[startNextBitsIndex..<endNextBitsIndex]
|
||||||
|
|
||||||
|
var number = nextBits.binaryToDecimal()
|
||||||
|
var reminder = bytes[endNextBitsIndex...]
|
||||||
|
var subPackets = [Packet]()
|
||||||
|
|
||||||
|
while number > 0 {
|
||||||
|
let (p, rem) = parsePacket(bytes: reminder)
|
||||||
|
number -= isTotalLength ? (reminder.count - rem.count) : 1
|
||||||
|
subPackets.append(p)
|
||||||
|
reminder = rem
|
||||||
|
}
|
||||||
|
return (subPackets, reminder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLiteralPacketValue<S>(bytes: S) -> (Int, S.SubSequence) where S:StringProtocol {
|
||||||
|
var index = bytes.index(bytes.startIndex, offsetBy: 6)
|
||||||
|
var ei = index
|
||||||
|
var bits: Substring = ""
|
||||||
|
var reminder: S.SubSequence = ""
|
||||||
|
while true {
|
||||||
|
let si = bytes.index(index, offsetBy: 1)
|
||||||
|
ei = bytes.index(si, offsetBy: 4)
|
||||||
|
bits += bytes[si..<ei]
|
||||||
|
if bytes[index] == "0" {
|
||||||
|
reminder = bytes[ei...]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
index = bytes.index(ei, offsetBy: 0)
|
||||||
|
}
|
||||||
|
return (bits.binaryToDecimal(), reminder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension StringProtocol {
|
||||||
|
func bits() -> [String] {
|
||||||
|
self.map { $0.bits }
|
||||||
|
}
|
||||||
|
|
||||||
|
func binaryToDecimal() -> Int { Int(self, radix: 2)! }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Character {
|
||||||
|
var bits: String {
|
||||||
|
switch self.hexDigitValue! {
|
||||||
|
case 0:
|
||||||
|
return "0000"
|
||||||
|
case 1:
|
||||||
|
return "0001"
|
||||||
|
case 2:
|
||||||
|
return "0010"
|
||||||
|
case 3:
|
||||||
|
return "0011"
|
||||||
|
case 4:
|
||||||
|
return "0100"
|
||||||
|
case 5:
|
||||||
|
return "0101"
|
||||||
|
case 6:
|
||||||
|
return "0110"
|
||||||
|
case 7:
|
||||||
|
return "0111"
|
||||||
|
case 8:
|
||||||
|
return "1000"
|
||||||
|
case 9:
|
||||||
|
return "1001"
|
||||||
|
case 10:
|
||||||
|
return "1010"
|
||||||
|
case 11:
|
||||||
|
return "1011"
|
||||||
|
case 12:
|
||||||
|
return "1100"
|
||||||
|
case 13:
|
||||||
|
return "1101"
|
||||||
|
case 14:
|
||||||
|
return "1110"
|
||||||
|
case 15:
|
||||||
|
return "1111"
|
||||||
|
default:
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Sources/16/main.swift
Normal file
12
Sources/16/main.swift
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// File.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Max Nuding on 15.12.21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Runner
|
||||||
|
|
||||||
|
//Runner(target: Day16.self, day: "16", isTest: true).run()
|
||||||
|
Runner(target: Day16.self, day: "16", isTest: false).run()
|
95
Sources/17/17.swift
Normal file
95
Sources/17/17.swift
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
//
|
||||||
|
// File.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Max Nuding on 17.12.21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Runner
|
||||||
|
import Collections
|
||||||
|
|
||||||
|
struct Point:Hashable {
|
||||||
|
var x: Int
|
||||||
|
var y: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Area {
|
||||||
|
let topLeft: Point
|
||||||
|
let botRight: Point
|
||||||
|
|
||||||
|
init(x: String, y: String) {
|
||||||
|
let xVals = x[x.index(x.startIndex, offsetBy: 2)...].components(separatedBy: "..").map { Int($0)! }
|
||||||
|
let yVals = y[y.index(y.startIndex, offsetBy: 2)...].components(separatedBy: "..").map { Int($0)! }
|
||||||
|
topLeft = Point(x: xVals.first!, y: yVals.last!)
|
||||||
|
botRight = Point(x: xVals.last!, y: yVals.first!)
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(point: Point) -> Bool {
|
||||||
|
point.x >= topLeft.x && point.x <= botRight.x && point.y <= topLeft.y && point.y >= botRight.y
|
||||||
|
}
|
||||||
|
|
||||||
|
func overshot(point: Point) -> Bool {
|
||||||
|
point.y < botRight.y || point.x > botRight.x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Probe {
|
||||||
|
var position = Point(x: 0, y: 0)
|
||||||
|
var velocity: Point
|
||||||
|
|
||||||
|
mutating func move() {
|
||||||
|
position.x += velocity.x
|
||||||
|
position.y += velocity.y
|
||||||
|
if velocity.x < 0 {
|
||||||
|
velocity.x += 1
|
||||||
|
} else if velocity.x > 0 {
|
||||||
|
velocity.x -= 1
|
||||||
|
}
|
||||||
|
velocity.y -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Day17: Runnable {
|
||||||
|
let inputPath: String
|
||||||
|
|
||||||
|
required init(inputPath: String) {
|
||||||
|
self.inputPath = inputPath
|
||||||
|
}
|
||||||
|
|
||||||
|
public func run() {
|
||||||
|
let input = try! String(contentsOfFile: inputPath)
|
||||||
|
let parts = input
|
||||||
|
.trimmingCharacters(in: .newlines)
|
||||||
|
.components(separatedBy: ": ")
|
||||||
|
.last!
|
||||||
|
.components(separatedBy: ", ")
|
||||||
|
let area = Area(x: parts.first!, y: parts.last!)
|
||||||
|
|
||||||
|
var maxY = Int.min
|
||||||
|
var found = [Point]()
|
||||||
|
found.reserveCapacity(3000)
|
||||||
|
for x in 0...500 {
|
||||||
|
for y in -500...500 {
|
||||||
|
let velocity = Point(x: x, y: y)
|
||||||
|
var probe = Probe(velocity: velocity)
|
||||||
|
var maxYCurrent = Int.min
|
||||||
|
repeat {
|
||||||
|
probe.move()
|
||||||
|
if probe.position.y > maxYCurrent {
|
||||||
|
maxYCurrent = probe.position.y
|
||||||
|
}
|
||||||
|
if area.contains(point: probe.position) {
|
||||||
|
if maxYCurrent > maxY {
|
||||||
|
maxY = maxYCurrent
|
||||||
|
}
|
||||||
|
found.append(velocity)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} while !area.overshot(point: probe.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print(maxY)
|
||||||
|
print(Set(found).count)
|
||||||
|
}
|
||||||
|
}
|
12
Sources/17/main.swift
Normal file
12
Sources/17/main.swift
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// File.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Max Nuding on 17.12.21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Runner
|
||||||
|
|
||||||
|
//Runner(target: Day17.self, day: "17", isTest: true).run()
|
||||||
|
Runner(target: Day17.self, day: "17", isTest: false).run()
|
1
Sources/Runner/Resources/input/16.txt
Normal file
1
Sources/Runner/Resources/input/16.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
020D708041258C0B4C683E61F674A1401595CC3DE669AC4FB7BEFEE840182CDF033401296F44367F938371802D2CC9801A980021304609C431007239C2C860400F7C36B005E446A44662A2805925FF96CBCE0033C5736D13D9CFCDC001C89BF57505799C0D1802D2639801A900021105A3A43C1007A1EC368A72D86130057401782F25B9054B94B003013EDF34133218A00D4A6F1985624B331FE359C354F7EB64A8524027D4DEB785CA00D540010D8E9132270803F1CA1D416200FDAC01697DCEB43D9DC5F6B7239CCA7557200986C013912598FF0BE4DFCC012C0091E7EFFA6E44123CE74624FBA01001328C01C8FF06E0A9803D1FA3343E3007A1641684C600B47DE009024ED7DD9564ED7DD940C017A00AF26654F76B5C62C65295B1B4ED8C1804DD979E2B13A97029CFCB3F1F96F28CE43318560F8400E2CAA5D80270FA1C90099D3D41BE00DD00010B893132108002131662342D91AFCA6330001073EA2E0054BC098804B5C00CC667B79727FF646267FA9E3971C96E71E8C00D911A9C738EC401A6CBEA33BC09B8015697BB7CD746E4A9FD4BB5613004BC01598EEE96EF755149B9A049D80480230C0041E514A51467D226E692801F049F73287F7AC29CB453E4B1FDE1F624100203368B3670200C46E93D13CAD11A6673B63A42600C00021119E304271006A30C3B844200E45F8A306C8037C9CA6FF850B004A459672B5C4E66A80090CC4F31E1D80193E60068801EC056498012804C58011BEC0414A00EF46005880162006800A3460073007B620070801E801073002B2C0055CEE9BC801DC9F5B913587D2C90600E4D93CE1A4DB51007E7399B066802339EEC65F519CF7632FAB900A45398C4A45B401AB8803506A2E4300004262AC13866401434D984CA4490ACA81CC0FB008B93764F9A8AE4F7ABED6B293330D46B7969998021C9EEF67C97BAC122822017C1C9FA0745B930D9C480
|
1
Sources/Runner/Resources/input/16_test.txt
Normal file
1
Sources/Runner/Resources/input/16_test.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
38006F45291200
|
1
Sources/Runner/Resources/input/17.txt
Normal file
1
Sources/Runner/Resources/input/17.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
target area: x=128..160, y=-142..-88
|
1
Sources/Runner/Resources/input/17_test.txt
Normal file
1
Sources/Runner/Resources/input/17_test.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
target area: x=20..30, y=-10..-5
|
Reference in New Issue
Block a user