Remove my-solutions, which were only included because of typo

This commit is contained in:
Matthias Portzel 2024-03-19 22:43:16 -04:00
parent 2a1b6cfe2d
commit ba659ddd97
15 changed files with 1 additions and 774 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
config/.env
my_solutions/
/my-solutions
puzzles.toml
*.sqlite3*

View File

@ -1,108 +0,0 @@
# frozen_string_literal: true
require "active_support/all"
require "pry"
class Set
alias :includes? :include?
end
class State
def initialize(flat)
@flat = flat
end
attr_reader :flat
def square
@square ||= @flat.in_groups_of 4
end
# Return the index of the empty square, 0-15
def empty
@empty ||= flat.index 0
end
def slide(dir)
# Let's assume the move is possible
if dir == :up
# index of the tile to move, so that's the tile below the current pos
# So we're going to grab empty and add three to push it to the next row
# And that's all good as long as this is a valid move
to_move_index = empty + 4
elsif dir == :down
to_move_index = empty - 4
elsif dir == :left
# Only valid if empty is in the first three columns
# So the index + 1 should work
to_move_index = empty + 1
elsif dir == :right
to_move_index = empty - 1
end
new_state = flat.dup
to_move = flat[to_move_index]
new_state[empty] = to_move
new_state[to_move_index] = 0
return State.new new_state
end
def possible_moves
valid = []
if empty < 12 # so 0 - 11, that is, the first three rows
valid.push :up
end
if empty >= 4
valid.push :down
end
# Can't go left if empty is in the last row
if empty % 4 != 3
valid.push :left
end
if empty % 4 != 0
valid.push :right
end
return valid
end
def adjacent_states
return possible_moves.map do |dir|
slide dir
end
end
def to_s
@s ||= flat.join ""
end
def dis
puts square.map(&:to_s).join("\n").sub("0", " ")
puts "\n"
end
end
seen_states = Set.new
# Last is actually the one we're going to work off of, so we can push and pop
unchecked_states = [State.new((0..15).to_a)]
while unchecked_states.length > 0
state = unchecked_states.pop
state.adjacent_states.each do |s|
unless seen_states.includes? s.to_s
seen_states.add s.to_s
unchecked_states.push s
end
end
end
# seen_states.count
# => 181440
binding.pry

View File

@ -1,42 +0,0 @@
// From /Users/matthias/Programs/tmp-name-js-plus/main.js
Object.defineProperty(Array.prototype, "chunks", {
value: function chunks (chunkSize) {
// TODO: Error on float
if (chunkSize<= 0) {
throw new Error("Window size must be a positive integer.");
}
if (this.length < chunkSize) {
return this.slice(0);
}
const accum = [];
for (let i = 0; i < this.length; i += chunkSize) {
accum.push(this.slice(i, i + chunkSize));
}
return accum;
}
});
const input = "W 3 N 5 I 2 W 3 E 2 R 5 O 1 I 5 S 2 S 8 O 2 I 8 W 5 W 6 W 2 W 1 E 7 O 9";
const i = input.split(" ").chunks(2)
let x = 0, y = 0, z = 0;
for (let [dir, dist] of i) {
dist = parseInt(dist, 10);
if (dir === "N") {
y -= dist;
} else if (dir === "S") {
y += dist;
} else if (dir === "E") {
x += dist;
} else if (dir === "W") {
x -= dist;
} else if (dir === "I") {
z += dist;
} else if (dir === "O") {
z -= dist;
}
}
console.log(`(${x}, ${y}, ${z})`)

View File

@ -1,108 +0,0 @@
# frozen_string_literal: true
require "active_support/all"
require "pry"
class Set
alias :includes? :include?
end
class State
def initialize(flat)
@flat = flat
end
attr_reader :flat
def square
@square ||= @flat.in_groups_of 3
end
# Return the index of the empty square, 0-8
def empty
@empty ||= flat.index 0
end
def slide(dir)
# Let's assume the move is possible
if dir == :up
# index of the tile to move, so that's the tile below the current pos
# So we're going to grab empty and add three to push it to the next row
# And that's all good as long as this is a valid move
to_move_index = empty + 3
elsif dir == :down
to_move_index = empty - 3
elsif dir == :left
# Only valid if empty is in the first two columns
# So the index + 1 should work
to_move_index = empty + 1
elsif dir == :right
to_move_index = empty - 1
end
new_state = flat.dup
to_move = flat[to_move_index]
new_state[empty] = to_move
new_state[to_move_index] = 0
return State.new new_state
end
def possible_moves
valid = []
if empty <= 5
valid.push :up
end
if empty >= 3
valid.push :down
end
# Can't go left if empty is in the last row
if empty % 3 != 2
valid.push :left
end
if empty % 3 != 0
valid.push :right
end
return valid
end
def adjacent_states
return possible_moves.map do |dir|
slide dir
end
end
def to_s
@s ||= flat.join ""
end
def dis
puts square.map(&:to_s).join("\n").sub("0", " ")
puts "\n"
end
end
seen_states = Set.new
# Last is actually the one we're going to work off of, so we can push and pop
unchecked_states = [State.new((0..8).to_a)]
while unchecked_states.length > 0
state = unchecked_states.pop
state.adjacent_states.each do |s|
unless seen_states.includes? s.to_s
seen_states.add s.to_s
unchecked_states.push s
end
end
end
# seen_states.count
# => 181440
binding.pry

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 MiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,18 +0,0 @@
require "active_support/all"
first_sundays = 0
start_date = Date.parse "1901-01-01"
end_date = Date.parse "2000-12-31"
date = start_date
while date < end_date
if date.day == 1 and date.sunday? then
first_sundays += 1
end
date = date.tomorrow
end
p first_sundays

View File

@ -1,15 +0,0 @@
require "vips"
im = Vips::Image.new_from_file "Linoleum.jpg"
binding.irb
count = 0
im.to_a.each do |row|
row.each do |pixel|
count += 1 if pixel == [52, 49, 53]
# p pixel if pixel[0] == 71
end
end
p count

View File

@ -1,208 +0,0 @@
# Authored 2023 Matthias Portzel. Public Domain
require "pry"
require "active_support/all"
require "ostruct"
class Direction
attr_accessor :name
attr_accessor :offset
def initialize(name, offset)
@name = name
@offset = OpenStruct.new x: offset[0], y: offset[1]
end
end
DIRECTIONS = [
Direction.new(:up_left, [-1, -1]),
Direction.new(:up, [0, -1]),
Direction.new(:up_right, [1, -1]),
Direction.new(:left, [-1, 0]),
Direction.new(:right, [1, 0]),
Direction.new(:down_left, [-1, +1]),
Direction.new(:down, [0, +1]),
Direction.new(:down_right, [+1, +1])
]
def for_xy (x_range, y_range, &block)
for x in x_range
for y in y_range
block.call x, y
end
end
end
class Cell
attr_accessor :is_alive
def alive?
@is_alive
end
def dead?
!@is_alive
end
for d in DIRECTIONS
attr_accessor d.name
end
attr_accessor :coords
def initialize(is_alive)
@is_alive = is_alive
@coords = OpenStruct.new x: nil, y: nil
end
def neighbors
DIRECTIONS.map do |d|
send d.name
end
end
end
class MobiusStrip
# 2D array of trues and falses
def initialize(grid)
@state = grid.map do |row|
row.map do |cell|
Cell.new cell
end
end
max_x = @state.first.length
max_y = @state.length
for_xy (0..(max_x-1)), (0..(max_y-1)) do |x, y|
cell = @state[y][x]
cell.coords.x = x
cell.coords.y = y
for d in DIRECTIONS
link_x = (x + d.offset.x) % max_x
link_y = (y + d.offset.y) % max_y
if x + d.offset.x == -1 || x + d.offset.x == max_x
link_y = ((max_y - 1) - link_y) % max_y
end
cell.send(d.name.to_s + "=", @state[link_y][link_x])
end
end
end
def grid
@state
end
def self.from_string(grid_string)
new(grid_string.split("\n").map(&:strip).map do |row|
row.split("").map do |cell|
cell == "#"
end
end)
end
def count_living_neighbors(cell)
cell.neighbors.filter(&:alive?).length
end
def step
max_x = @state.first.length
max_y = @state.length
new_grid = Array.new(max_y).map { Array.new(max_x) }
for_xy (0..(max_x-1)), (0..(max_y-1)) do |x, y|
cell = @state[y][x]
live_neighbors = count_living_neighbors(@state[y][x])
# Cell alive, and 0..1 live neighbors, die
# Cell alive, 4.. live neighbors, die
# Cell alive, no change
# Cell dead, 3 live neighbors, live
# Cell dead, no change
new_state = if cell.alive? && (0..1) === live_neighbors then
false
elsif cell.alive? && live_neighbors >= 4 then
false
elsif cell.alive? then
true
elsif cell.dead? && live_neighbors == 3 then
true
elsif cell.dead?
false
end
# p [cell.coords, cell.alive?.to_s, live_neighbors.to_s, new_state.to_s].join ", "
new_grid[y][x] = new_state
end
MobiusStrip.new new_grid
end
def display
lines = @state.map do |row|
chars = row.map do |cell|
if cell.alive? then "#" else "." end
end
chars.join ""
end
lines.join "\n"
end
end
test_board = MobiusStrip.from_string(<<-EOF
..........
..........
........#.
........#.
#.#..#..#.
#.#.#.#...
.##..##.#.
..#.......
##........
..........
EOF
)
board = MobiusStrip.from_string(<<-EOF
....................
............###.....
.....#.#...#...#....
...........#........
.....#.#...#........
......#....#.###....
...........#...#....
...##......#...#....
...##.......###.....
....................
....................
...#####...#...#....
...#.......#...#....
...#.......#...#....
...#####...#####....
...#.......#...#....
...#.......#...#....
...#.......#...#....
...#.......#...#....
....................
EOF
)
# board = test_board
past_boards = []
day = 0
loop do
board_string = board.display
if past_boards.include? board_string
after_days = day - 1
puts "Repetition after day #{after_days}"
break
end
puts "On day #{day}:"
puts board_string
past_boards << board_string
board = board.step
day += 1
end

View File

@ -1,12 +0,0 @@
const a = {b: 2, c: 3};
Object.defineProperty(Object.prototype, "with_key_methods", {
value: function () {
for (const {k: v} in this) {
console.log(k, v);
// this[k] = () => v;
}
}
});
a.with_key_methods().b()

View File

@ -1 +0,0 @@
{"type":"module"}

View File

@ -1,37 +0,0 @@
// What is the second smallest prime number:
// * whose digits are all odd,
// * greater than 1057
// * whose palindrome is also an odd number
const primes = [];
function isPrime(num) {
if (primes.includes(num)) {
return true;
}else {
const s = sqrt(num);
for (const i = 0; i < s; i++) {
if (num % i === 0) {
return false;
}
}
primes.push(num);
return true;
}
}
Number.prototype.digits = function () {
return this.toString().split().reverse().map(d => parseInt(d, 10));
}
Number.prototype.isOdd = function () {
return this % 2 === 1;
}
const numFound = 0;
for (const i = 1057; numFound < 2; i++) {
if (isPrime(i) && i.digits().all()) {
}
}

View File

@ -1,17 +0,0 @@
require 'prime'
# // What is the second smallest prime number:
# // * whose digits are all odd,
# // * greater than 1057
# // * whose palindrome is also an odd number
numFound = 0
i = 1057
while numFound < 30 do
if i.prime? && i.digits.join.to_i.prime?
p i
numFound += 1
end
i += 1
end

View File

@ -1,182 +0,0 @@
// Going to regret doing this in JS but whatever
import "/Users/matthias/Programs/tmp-name-js-plus/main.js"
/*
const points = [];
for (let i = 0; i < 33; i++) {
points.push({
x: Math.floor(Math.random() * 100),
y: Math.floor(Math.random() * 100)
});
}
const seen = new Set();
for (const point of points) {
seen.add(JSON.stringify(point));
}
points.clear();
for (const point of seen) {
points.push(JSON.parse(point));
}
*/
const points = [
{ x: 21, y: 21 }, { x: 94, y: 81 },
{ x: 22, y: 32 }, { x: 98, y: 96 },
{ x: 12, y: 94 }, { x: 57, y: 58 },
{ x: 31, y: 30 }, { x: 56, y: 7 },
{ x: 60, y: 1 }, { x: 27, y: 47 },
{ x: 74, y: 55 }, { x: 53, y: 70 },
{ x: 48, y: 74 }, { x: 20, y: 68 },
// { x: 47, y: 70 }, { x: 51, y: 27 },
// { x: 93, y: 27 }, { x: 31, y: 88 },
// { x: 25, y: 36 }, { x: 28, y: 31 },
// { x: 44, y: 33 }, { x: 18, y: 56 },
// { x: 80, y: 18 }, { x: 44, y: 65 },
// { x: 0, y: 61 }, { x: 57, y: 55 },
// { x: 39, y: 29 }, { x: 4, y: 39 },
// { x: 83, y: 59 }, { x: 9, y: 38 },
// { x: 28, y: 2 }, { x: 28, y: 73 },
// { x: 72, y: 40 }
];
// At least 3 points in each loop
// A*?
// I'm nervous because this is a really poor match for A* on the face of it; A* excels at deep problems, but this is *really* shallow. Maybe that's fine? Because it's so shallow it shouldn't be that hard to finish any particular branch.
// But we also might hit memory limitations.
// My intuition tells me brute force will be just out of reach. 33! solutions? / 3 for symmetry between the 3 loops. I think. 3 e 36.
// At least several days
// let l = 0
// for (let i = 0; i < 2.894439206E36; i ++) {
// l += i;
// if (i % 100000000 === 0) {
// console.log(i / 2.894439206E36)
// }
// }
// console.log(l);
// I'm going to give A* a shot
// A node is a configuration of assigned loops and any unassigned points
// A*'s nice because as soon as we find one solution that assigns all points, we have the minimum
// A* is best-first + heuristic, with the score being the current loop distance
// The problem is we don't have a good heuristic
// Let's run best-first and see what happens
function distance (point1_id, point2_id) {
const point1 = points[point1_id];
const point2 = points[point2_id];
return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);
}
class State {
constructor () {
// super(); ???
// Three loops. Each one is a list of points
// "point" refers to the index of the point in points, we're not copying them
this.loops = [[], [], []];
this.unassignedPoints = Array.range(points.length);
// We can start off with the first point in the first loop
this.assign(0, 0);
}
assign (point_i, loop_i) {
this.loops[loop_i].push(point_i);
this.unassignedPoints.remove(point_i);
this.updateScore();
}
isComplete () {
return this.unassignedPoints.length === 0;
}
getIncompleteScore () {
let score = 0;
for (const loop of this.loops) {
loop.windows(2).forEach(function (pair) {
score += distance(...pair);
});
}
return score;
}
getCompleteScore () {
// The complete score is just the incomplete score + the distance to "close", from the last point to the first one
let score = this.getIncompleteScore();
for (const loop of loops) {
score += distance(loop.last, loop.first);
}
return score;
}
// Get the score
getScore () {
return this.isComplete() ? this.getCompleteScore() : this.getIncompleteScore();
}
updateScore () {
this.score = this.getScore();
}
clone () {
// This creates a new initial State, but that's fine
const state = new State();
state.unassignedPoints = this.unassignedPoints.copy();
for (let i = 0; i < this.loops.length; i ++) {
state.loops[i] = this.loops[i].copy();
}
state.updateScore();
return state;
}
// Return all possible next states
getNextStates () {
// if there aren't any, return []
if (this.isComplete()) {
return [];
}
const nextStates = [];
// Otherwise, we have the option of assigning any unassigned point to any of the three loops
for (let i = 0; i < this.unassignedPoints.length; i++) {
for (let j = 0; j < this.loops.length; j++) {
const state = this.clone();
state.assign(this.unassignedPoints[i], j);
nextStates.push(state);
}
}
return nextStates;
}
}
// Sorted by score of course
// Best score at the end, so we can .pop() it
// Best score is lowest of course
const states = [new State()];
// console.log(states.first);
// console.log(states.first.getNextStates());
// Insert 1 or more states preserving sort order
function insertState (...states) {
for (const state of states) {
// i is the index that the state will end up at
// If we're inserting at the end a lot, running this loop in reverse may be worth it
let i = 0;
while (i < states.length && states[i].score > state.score) {
i ++;
}
states.splice(i, 0, state);
}
}
while (!states.last.isComplete()) {
// process a state
const state = states.pop();
insertState(...state.getNextStates());
}
print(states);

View File

@ -1,24 +0,0 @@
require "nokogiri"
f = File.read("Linoleumherstellung.svg")
xml = Nokogiri::XML(f)
# binding.irb
# stack = [xml]
# max_depth = 0
# until stack.empty?
# end
# Height is the number of edges, so a tree with two nodes has height 1
# <foo> <bar> </bar> </foo>
def max_depth(tree)
return 0 if tree.children.empty?
return 1 + (tree.children.map { |c| max_depth c }).max
end
p max_depth(xml)