all stats

LyricLy's stats

guessed the most

namecorrect guessesgames togetherratio
Kit441.000
MattiDragon441.000
seshoumara12121.000
olus200027330.818
HelloBoi450.800
SoundOfSpouting790.778
chirk8120.667
vspf350.600
essaie6100.600
Dolphy9150.600
Olivia25430.581
yui470.571
taswelll8150.533
Kaylynn240.500
kimapr11220.500
Palaiologos14290.483
luatic9220.409
JJRubes4100.400
Camto250.400
quintopia4100.400
BeatButton260.333
IFcoltransG9280.321
moshikoi6200.300
gollark3110.273
Olive3110.273
Moja140.250
soup girl4160.250
ultlang140.250
at140.250
olive290.222
razetime8370.216
GNU Radio Shows3140.214
deadbraincoral040.000
Edgex42060.000
citrons050.000
theqwertiest050.000

were guessed the most by

namecorrect guessesgames togetherratio
gollark5100.500
Dolphy7140.500
GNU Radio Shows6140.429
luatic9220.409
kimapr9220.409
Palaiologos9260.346
olus200012350.343
olive390.333
at140.250
Kit140.250
Moja140.250
SoundOfSpouting290.222
Olive290.222
Olivia9410.220
soup girl3140.214
quintopia2100.200
taswelll3150.200
Camto150.200
chirk2110.182
razetime6350.171
Edgex42160.167
seshoumara2120.167
IFcoltransG4250.160
moshikoi3190.158
essaie1100.100
JJRubes1100.100
theqwertiest050.000
HelloBoi040.000
ultlang040.000
deadbraincoral040.000
MattiDragon040.000
BeatButton040.000
yui070.000
vspf050.000

entries

round #67

submitted at
0 likes

guesses
comments 0

post a comment


i_hate_myself.py ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import itertools
from collections import Counter, defaultdict

def find_straight(many):
    run_next = None
    have_ace = False
    for key, _ in itertools.groupby(many):
        if key == 14:
            have_ace = True
        if run_next == key:
            if run_length == 4 or run_length == 3 and key == 2 and have_ace:
                return run_next + run_length
        else:
            run_length = 0
        run_length += 1
        run_next = key - 1

# GOD THIS FLUMMERY IS SO TARNATION ANNOYING
def eval_hand(cards):
    cards.sort(reverse=True)
    ranks = [card[0] for card in cards]

    # flushes
    by_suit = defaultdict(list)
    for card in cards:
        suit = by_suit[card[1]]
        suit.append(card[0])
        if len(suit) == 5:
            # FLUSH!
            if straight := find_straight(suit):
                # STRAIGHT FLUSH!!!
                return [5], straight
            return [3, 1.75], suit

    # straights
    if straight := find_straight(ranks):
        return [3, 1.25], straight

    # everything else
    counts = []
    values = []
    s = 0
    for value, count in Counter(ranks).most_common():
        count = min(count, 5-s)
        if not count:
            break
        s += count
        counts.append(count)
        values.append(value)
    return counts, values

def entry(hands, commune, out_of_play=()):
    deck = set(itertools.product(range(2, 15), "HDCS"))
    deck.difference_update(out_of_play)
    deck.difference_update(commune)
    for hand in hands:
        deck.difference_update(hand)

    c = 0
    winners = Counter()
    for rest in itertools.combinations(deck, 5 - len(commune)):
        c += 1
        best_hand = None
        winner = None
        for idx, hand in enumerate(hands):
            us = eval_hand([*commune, *rest, *hand]) 
            if not best_hand or us > best_hand:
                best_hand = us
                winner = idx
            elif us == best_hand:
                winner = None
        if winner is not None:
            winners[winner] += 1

    return {x: y / c for x, y in winners.items()}

round #66

submitted at
0 likes

guesses
comments 2
LyricLy known at the time as [author of #6]

i accidentally provided the indev version instead of the release copy my apologies


LyricLy known at the time as [author of #6]

use this function to fix it, LyricLy

def fixer(s):
    return "".join(s.splitlines(True)[:-4])

post a comment


believe.py ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def entry():
    yield 1
    b = [1]
    while True:
        beginning = len(b)
        yield from b
        b.extend(b)
        yield 2
        b.append(2)
        while True:
            best = 0
            for l in range(1, (len(b) - beginning) // 2 + 1):
                c = 0
                i = len(b)
                start = b[-l:]
                while True:
                    i -= l
                    c += 1
                    if i < beginning or b[i-l:i] != start:
                        break
                best = max(best, c)
            if best == 1:
                break
            yield best
            b.append(best)

round #65

submitted at
0 likes

guesses
comments 0

post a comment


reprehensiblensible.py ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import numpy as np

def sum_tests(f, arr, chop_x, chop_y):
    piece = arr[chop_x:, chop_y:]
    s = 0
    for cx, i in enumerate(range(chop_x, 3)):
        for cy, j in enumerate(range(chop_y, 3)):
            if not (i or j):
                continue
            t = np.pad(piece[:-cx or None, :-cy or None], ((i, 0), (j, 0)))
            s += f(t)
    return s

def f(arr):
    if arr[1, 1]:
        return arr[1, 2]
    else:
        return arr[1, 0]

def is_nc(f):
    for n in range(512):
        arr = (n & 1 << np.arange(9).reshape((3, 3))).astype(bool)
        if (f(arr) !=
            arr[0, 0]
            + sum_tests(f, arr, True, False)
            + sum_tests(f, arr, False, True)
            - sum_tests(f, arr, False, False)
            - sum_tests(f, arr, True, True)
           ):
            return False
    return True

def fofn(n):
    def f(arr):
        return (n >> int(np.sum(arr * (1 << np.arange(9).reshape((3, 3)))))) & 1
    return f

def entry(n):
    return is_nc(fofn(n))

round #64

submitted at
1 like

guesses
comments 0

post a comment


the humble meee ASCII text, with no line terminators
1
1[&sJu52*ol[s>l<0s`[pl`sp=]p>l<]>r

round #63

submitted at
0 likes

guesses
comments 0

post a comment


my points empty

round #62

submitted at
3 likes

guesses
comments 0

post a comment


sokohen.py Unicode text, UTF-8 text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
import argparse
import signal
import sys
from blessed import Terminal

PH = "⬚"

parser = argparse.ArgumentParser(description="Edit a file.")
parser.add_argument("filename")
args = parser.parse_args()

buffer = []

def load(f):
    content = f.read()
    if not content:
        buffer.clear()
        return
    if any(ord(c) not in {10, *range(32, 126)} for c in content):
        raise ValueError("file is not printable ASCII")
    buffer[:] = [list(x + PH*x.endswith(" ")) for x in content.split("\n")]
    if not buffer[0]:
        buffer[0].append(PH)
    if not buffer[-1]:
        buffer[-1].append(PH)

def save(f):
    for i, line in enumerate(buffer):
        end = "\n" if i + 1 != len(buffer) else ""
        f.write("".join(c for c in line if c != PH) + end)

def at(y, x):
    y += origin
    if 0 <= y < len(buffer) and 0 <= x < len(buffer[y]):
        return buffer[y][x]
    else:
        return " "

def put(y, x, c):
    global origin
    if x < 0:
        return
    if not buffer:
        origin = -y
    if c != " ":
        y += origin
        prepend_by = max(-y, 0)
        origin += prepend_by
        y += prepend_by
        buffer[:0] = [[] for _ in range(prepend_by)]
        buffer.extend([] for _ in range(max(y-len(buffer)+1, 0)))
        line = buffer[y]
        line.extend(" " * max(x-len(line)+1, 0))
        line[x] = c
    else:
        if at(y, x) == " ":
            return
        clear_off.append((y, x))
        y += origin
        line = buffer[y]
        line[x] = " "
        while line and line[-1] == " ":
            line.pop()
        while buffer and not buffer[0]:
            origin -= 1
            buffer.pop(0)
        while buffer and not buffer[-1]:
            buffer.pop()

try:
    with open(args.filename) as f:
        load(f)
except FileNotFoundError:
    pass
except ValueError:
    print("oh no: file does not consist of printable ASCII", file=sys.stderr)
    sys.exit(1)

clear_off = []
origin = -1
uy = ux = cy = cx = 0
at_keyboard = False

term = Terminal()
norm = term.bright_white + term.on_color_rgb(0x26, 0x26, 0x26)
sb = term.white + term.on_color_rgb(0x36, 0x36, 0x36)

def total_clear():
    print(norm + term.clear)
    clear_off.clear()

def render():
    global cy, cx, ux

    o = cy, cx
    h = term.height
    cy += (uy-cy) // h * h
    s = cy + origin
    nw = len(str(max(s, 0) + h))

    if not at_keyboard and ux < ~nw:
        add_keyboard()
    if at_keyboard and ux >= at_keyboard:
        remove_keyboard()
    if at_keyboard:
        nw = -1
        ux = max(ux, 0)

    w = term.width - nw - 1
    cx += (ux-cx) // w * w
    cx = max(cx, 0)

    if (cy, cx) != o:
        total_clear()

    with term.hidden_cursor():
        print(norm, end="")
        for y, x in clear_off:
            print(term.move_yx(y - cy, x - cx + nw+1) + " ", end="")
        clear_off.clear()

        print(term.home, end="")
        for dry in range(s, s+h):
            end = "\r\n" if dry + 1 != s+h else ""
            if 0 <= dry < len(buffer):
                line = "".join(buffer[dry][cx:cx+w])
                if not at_keyboard:
                    print(f"{sb}{dry+1:>{nw}} ", end="")
                print(f"{norm}{line}", end=end)
            else:
                print("" if at_keyboard else sb + " "*(nw+1), end=end)

        print(term.move_yx(uy - cy, ux - cx + nw+1), end="", flush=True)

def on_resize(sig, action):
    render()

signal.signal(signal.SIGWINCH, on_resize)

def shift(dy, dx):
    global uy, ux
    uy += dy
    ux += dx
    y = uy
    x = ux
    shifting = " "
    while at(y, x) != " ":
        temp = at(y, x)
        put(y, x, shifting)
        shifting = temp
        y += dy
        x += dx
    put(y, x, shifting)

KEYBOARD = rf"""
` ~ 1 ! 2 @ 3 # 4 $ 5 % 6 ^ 7 & 8 * 9 ( 0 ) - _ = +

     q Q w W e E r R t T y Y u U i I o O p P [ {{ ] }} \ |

      a A s S d D f F g G h H j J k K l L ; : ' "

       z Z x X c C v V b B n N m M , < . > / ? {PH}
"""

def add_keyboard():
    global at_keyboard, ux
    shift = term.width + ~ux
    at_keyboard = shift
    ux += shift
    for l in buffer:
        if l:
            l[:0] = [" "] * shift
    rows = KEYBOARD.strip().splitlines()
    top = term.height // 2 - len(rows) // 2
    left = term.width // 2 - len(rows[2]) // 2
    for y, row in enumerate(rows):
        for x, c in enumerate(row):
            put(top+y, left+x, c) 
    total_clear()

def remove_keyboard():
    global at_keyboard, ux
    for y, l in enumerate(buffer):
        for x in range(0, at_keyboard):
            put(y-origin, x, " ")
        del l[:at_keyboard]
    ux -= at_keyboard
    at_keyboard = None

def ctrl(c):
    return chr(ord(c) & 31)

with term.fullscreen(), term.raw():
    total_clear()
    render()
    while True:
        next_key = term.inkey()
        if next_key == ctrl("q"):
            break
        elif next_key == ctrl("s"):
            with open(args.filename, "w") as f:
                save(f)
        elif next_key.code == term.KEY_UP:
            shift(-1, 0)
        elif next_key.code == term.KEY_DOWN:
            shift(1, 0)
        elif next_key.code == term.KEY_RIGHT:
            shift(0, 1)
        elif next_key.code == term.KEY_LEFT:
            shift(0, -1)
        render()
tut.txt ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Welcome to sokohen! (Edit this file with `python sokohen.py tut.txt`.)

This editor works like the game Sokoban. Use arrow keys to move your cursor and push letters around!

  Youcanmove your cursor through the line number column. Try moving this entire sentence to the right, so it looks like the line below.
                        Youcanmoveyourcursorthroughthelinenumbercolumn.Trymovingthisentiresentencetotheright,soitlookslikethelinebelow.

Go all the way through the line numbers to the screen to the left to get a keyboard you can take letters from.
The keyboard automatically refreshes whenever you return to its screen, so characters are renewable.

Finally, the symbol below is a "placeholder" that represents... nothing at all! You can use it to insert trailing spaces or leading/trailing newlines. Have fun with sokohen!









                       

round #61

submitted at
0 likes

guesses
comments 0

post a comment


soln.py ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from random import *
while True:
  g=[]
  while r:=input():g.append(r)
  x=int(input());y=int(input())
  me=g[y][x]
  for l,dx,dy in [("U",0,-1),("R",1,0),("D",0,1),("L",-1,0)]:
    them=g[y+dy][x+dx]
    if (ord(me)+ord(them))%3 == 2:
        print(f"I\n{l}")
        break
  else:
    print(f"M\n{choice("URDL")}")

round #60

submitted at
3 likes

guesses
comments 0

post a comment


dir floss
dir .git
dir hooks
applypatch-msg.sample ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/sh
#
# An example hook script to check the commit log message taken by
# applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit.  The hook is
# allowed to edit the commit message file.
#
# To enable this hook, rename this file to "applypatch-msg".

. git-sh-setup
commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
:
commit-msg.sample ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/sh
#
# An example hook script to check the commit log message.
# Called by "git commit" with one argument, the name of the file
# that has the commit message.  The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit.  The hook is allowed to edit the commit message file.
#
# To enable this hook, rename this file to "commit-msg".

# Uncomment the below to add a Signed-off-by line to the message.
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
# hook is more suited to it.
#
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"

# This example catches duplicate Signed-off-by lines.

test "" = "$(grep '^Signed-off-by: ' "$1" |
	 sort | uniq -c | sed -e '/^[ 	]*1[ 	]/d')" || {
	echo >&2 Duplicate Signed-off-by lines.
	exit 1
}
fsmonitor-watchman.sample Perl script text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#!/usr/bin/perl

use strict;
use warnings;
use IPC::Open2;

# An example hook script to integrate Watchman
# (https://facebook.github.io/watchman/) with git to speed up detecting
# new and modified files.
#
# The hook is passed a version (currently 2) and last update token
# formatted as a string and outputs to stdout a new update token and
# all files that have been modified since the update token. Paths must
# be relative to the root of the working tree and separated by a single NUL.
#
# To enable this hook, rename this file to "query-watchman" and set
# 'git config core.fsmonitor .git/hooks/query-watchman'
#
my ($version, $last_update_token) = @ARGV;

# Uncomment for debugging
# print STDERR "$0 $version $last_update_token\n";

# Check the hook interface version
if ($version ne 2) {
	die "Unsupported query-fsmonitor hook version '$version'.\n" .
	    "Falling back to scanning...\n";
}

my $git_work_tree = get_working_dir();

my $retry = 1;

my $json_pkg;
eval {
	require JSON::XS;
	$json_pkg = "JSON::XS";
	1;
} or do {
	require JSON::PP;
	$json_pkg = "JSON::PP";
};

launch_watchman();

sub launch_watchman {
	my $o = watchman_query();
	if (is_work_tree_watched($o)) {
		output_result($o->{clock}, @{$o->{files}});
	}
}

sub output_result {
	my ($clockid, @files) = @_;

	# Uncomment for debugging watchman output
	# open (my $fh, ">", ".git/watchman-output.out");
	# binmode $fh, ":utf8";
	# print $fh "$clockid\n@files\n";
	# close $fh;

	binmode STDOUT, ":utf8";
	print $clockid;
	print "\0";
	local $, = "\0";
	print @files;
}

sub watchman_clock {
	my $response = qx/watchman clock "$git_work_tree"/;
	die "Failed to get clock id on '$git_work_tree'.\n" .
		"Falling back to scanning...\n" if $? != 0;

	return $json_pkg->new->utf8->decode($response);
}

sub watchman_query {
	my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty')
	or die "open2() failed: $!\n" .
	"Falling back to scanning...\n";

	# In the query expression below we're asking for names of files that
	# changed since $last_update_token but not from the .git folder.
	#
	# To accomplish this, we're using the "since" generator to use the
	# recency index to select candidate nodes and "fields" to limit the
	# output to file names only. Then we're using the "expression" term to
	# further constrain the results.
	my $last_update_line = "";
	if (substr($last_update_token, 0, 1) eq "c") {
		$last_update_token = "\"$last_update_token\"";
		$last_update_line = qq[\n"since": $last_update_token,];
	}
	my $query = <<"	END";
		["query", "$git_work_tree", {$last_update_line
			"fields": ["name"],
			"expression": ["not", ["dirname", ".git"]]
		}]
	END

	# Uncomment for debugging the watchman query
	# open (my $fh, ">", ".git/watchman-query.json");
	# print $fh $query;
	# close $fh;

	print CHLD_IN $query;
	close CHLD_IN;
	my $response = do {local $/; <CHLD_OUT>};

	# Uncomment for debugging the watch response
	# open ($fh, ">", ".git/watchman-response.json");
	# print $fh $response;
	# close $fh;

	die "Watchman: command returned no output.\n" .
	"Falling back to scanning...\n" if $response eq "";
	die "Watchman: command returned invalid output: $response\n" .
	"Falling back to scanning...\n" unless $response =~ /^\{/;

	return $json_pkg->new->utf8->decode($response);
}

sub is_work_tree_watched {
	my ($output) = @_;
	my $error = $output->{error};
	if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {
		$retry--;
		my $response = qx/watchman watch "$git_work_tree"/;
		die "Failed to make watchman watch '$git_work_tree'.\n" .
		    "Falling back to scanning...\n" if $? != 0;
		$output = $json_pkg->new->utf8->decode($response);
		$error = $output->{error};
		die "Watchman: $error.\n" .
		"Falling back to scanning...\n" if $error;

		# Uncomment for debugging watchman output
		# open (my $fh, ">", ".git/watchman-output.out");
		# close $fh;

		# Watchman will always return all files on the first query so
		# return the fast "everything is dirty" flag to git and do the
		# Watchman query just to get it over with now so we won't pay
		# the cost in git to look up each individual file.
		my $o = watchman_clock();
		$error = $output->{error};

		die "Watchman: $error.\n" .
		"Falling back to scanning...\n" if $error;

		output_result($o->{clock}, ("/"));
		$last_update_token = $o->{clock};

		eval { launch_watchman() };
		return 0;
	}

	die "Watchman: $error.\n" .
	"Falling back to scanning...\n" if $error;

	return 1;
}

sub get_working_dir {
	my $working_dir;
	if ($^O =~ 'msys' || $^O =~ 'cygwin') {
		$working_dir = Win32::GetCwd();
		$working_dir =~ tr/\\/\//;
	} else {
		require Cwd;
		$working_dir = Cwd::cwd();
	}

	return $working_dir;
}
post-update.sample ASCII text
1
2
3
4
5
6
7
8
#!/bin/sh
#
# An example hook script to prepare a packed repository for use over
# dumb transports.
#
# To enable this hook, rename this file to "post-update".

exec git update-server-info
pre-applypatch.sample ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/sh
#
# An example hook script to verify what is about to be committed
# by applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-applypatch".

. git-sh-setup
precommit="$(git rev-parse --git-path hooks/pre-commit)"
test -x "$precommit" && exec "$precommit" ${1+"$@"}
:
pre-commit.sample ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments.  The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".

if git rev-parse --verify HEAD >/dev/null 2>&1
then
	against=HEAD
else
	# Initial commit: diff against an empty tree object
	against=$(git hash-object -t tree /dev/null)
fi

# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --type=bool hooks.allownonascii)

# Redirect output to stderr.
exec 1>&2

# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
	# Note that the use of brackets around a tr range is ok here, (it's
	# even required, for portability to Solaris 10's /usr/bin/tr), since
	# the square bracket bytes happen to fall in the designated range.
	test $(git diff-index --cached --name-only --diff-filter=A -z $against |
	  LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
	cat <<\EOF
Error: Attempt to add a non-ASCII file name.

This can cause problems if you want to work with people on other platforms.

To be portable it is advisable to rename the file.

If you know what you are doing you can disable this check using:

  git config hooks.allownonascii true
EOF
	exit 1
fi

# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --
pre-merge-commit.sample ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git merge" with no arguments.  The hook should
# exit with non-zero status after issuing an appropriate message to
# stderr if it wants to stop the merge commit.
#
# To enable this hook, rename this file to "pre-merge-commit".

. git-sh-setup
test -x "$GIT_DIR/hooks/pre-commit" &&
        exec "$GIT_DIR/hooks/pre-commit"
:
pre-push.sample ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/bin/sh

# An example hook script to verify what is about to be pushed.  Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed.  If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
#   <local ref> <local oid> <remote ref> <remote oid>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).

remote="$1"
url="$2"

zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')

while read local_ref local_oid remote_ref remote_oid
do
	if test "$local_oid" = "$zero"
	then
		# Handle delete
		:
	else
		if test "$remote_oid" = "$zero"
		then
			# New branch, examine all commits
			range="$local_oid"
		else
			# Update to existing branch, examine new commits
			range="$remote_oid..$local_oid"
		fi

		# Check for WIP commit
		commit=$(git rev-list -n 1 --grep '^WIP' "$range")
		if test -n "$commit"
		then
			echo >&2 "Found WIP commit in $local_ref, not pushing"
			exit 1
		fi
	fi
done

exit 0
pre-rebase.sample ASCII text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/bin/sh
#
# Copyright (c) 2006, 2008 Junio C Hamano
#
# The "pre-rebase" hook is run just before "git rebase" starts doing
# its job, and can prevent the command from running by exiting with
# non-zero status.
#
# The hook is called with the following parameters:
#
# $1 -- the upstream the series was forked from.
# $2 -- the branch being rebased (or empty when rebasing the current branch).
#
# This sample shows how to prevent topic branches that are already
# merged to 'next' branch from getting rebased, because allowing it
# would result in rebasing already published history.

publish=next
basebranch="$1"
if test "$#" = 2
then
	topic="refs/heads/$2"
else
	topic=`git symbolic-ref HEAD` ||
	exit 0 ;# we do not interrupt rebasing detached HEAD
fi

case "$topic" in
refs/heads/??/*)
	;;
*)
	exit 0 ;# we do not interrupt others.
	;;
esac

# Now we are dealing with a topic branch being rebased
# on top of master.  Is it OK to rebase it?

# Does the topic really exist?
git show-ref -q "$topic" || {
	echo >&2 "No such branch $topic"
	exit 1
}

# Is topic fully merged to master?
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
if test -z "$not_in_master"
then
	echo >&2 "$topic is fully merged to master; better remove it."
	exit 1 ;# we could allow it, but there is no point.
fi

# Is topic ever merged to next?  If so you should not be rebasing it.
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
only_next_2=`git rev-list ^master           ${publish} | sort`
if test "$only_next_1" = "$only_next_2"
then
	not_in_topic=`git rev-list "^$topic" master`
	if test -z "$not_in_topic"
	then
		echo >&2 "$topic is already up to date with master"
		exit 1 ;# we could allow it, but there is no point.
	else
		exit 0
	fi
else
	not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
	/usr/bin/perl -e '
		my $topic = $ARGV[0];
		my $msg = "* $topic has commits already merged to public branch:\n";
		my (%not_in_next) = map {
			/^([0-9a-f]+) /;
			($1 => 1);
		} split(/\n/, $ARGV[1]);
		for my $elem (map {
				/^([0-9a-f]+) (.*)$/;
				[$1 => $2];
			} split(/\n/, $ARGV[2])) {
			if (!exists $not_in_next{$elem->[0]}) {
				if ($msg) {
					print STDERR $msg;
					undef $msg;
				}
				print STDERR " $elem->[1]\n";
			}
		}
	' "$topic" "$not_in_next" "$not_in_master"
	exit 1
fi

<<\DOC_END

This sample hook safeguards topic branches that have been
published from being rewound.

The workflow assumed here is:

 * Once a topic branch forks from "master", "master" is never
   merged into it again (either directly or indirectly).

 * Once a topic branch is fully cooked and merged into "master",
   it is deleted.  If you need to build on top of it to correct
   earlier mistakes, a new topic branch is created by forking at
   the tip of the "master".  This is not strictly necessary, but
   it makes it easier to keep your history simple.

 * Whenever you need to test or publish your changes to topic
   branches, merge them into "next" branch.

The script, being an example, hardcodes the publish branch name
to be "next", but it is trivial to make it configurable via
$GIT_DIR/config mechanism.

With this workflow, you would want to know:

(1) ... if a topic branch has ever been merged to "next".  Young
    topic branches can have stupid mistakes you would rather
    clean up before publishing, and things that have not been
    merged into other branches can be easily rebased without
    affecting other people.  But once it is published, you would
    not want to rewind it.

(2) ... if a topic branch has been fully merged to "master".
    Then you can delete it.  More importantly, you should not
    build on top of it -- other people may already want to
    change things related to the topic as patches against your
    "master", so if you need further changes, it is better to
    fork the topic (perhaps with the same name) afresh from the
    tip of "master".

Let's look at this example:

		   o---o---o---o---o---o---o---o---o---o "next"
		  /       /           /           /
		 /   a---a---b A     /           /
		/   /               /           /
	       /   /   c---c---c---c B         /
	      /   /   /             \         /
	     /   /   /   b---b C     \       /
	    /   /   /   /             \     /
    ---o---o---o---o---o---o---o---o---o---o---o "master"


A, B and C are topic branches.

 * A has one fix since it was merged up to "next".

 * B has finished.  It has been fully merged up to "master" and "next",
   and is ready to be deleted.

 * C has not merged to "next" at all.

We would want to allow C to be rebased, refuse A, and encourage
B to be deleted.

To compute (1):

	git rev-list ^master ^topic next
	git rev-list ^master        next

	if these match, topic has not merged in next at all.

To compute (2):

	git rev-list master..topic

	if this is empty, it is fully merged to "master".

DOC_END
pre-receive.sample ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/sh
#
# An example hook script to make use of push options.
# The example simply echoes all push options that start with 'echoback='
# and rejects all pushes when the "reject" push option is used.
#
# To enable this hook, rename this file to "pre-receive".

if test -n "$GIT_PUSH_OPTION_COUNT"
then
	i=0
	while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
	do
		eval "value=\$GIT_PUSH_OPTION_$i"
		case "$value" in
		echoback=*)
			echo "echo from the pre-receive-hook: ${value#*=}" >&2
			;;
		reject)
			exit 1
		esac
		i=$((i + 1))
	done
fi
prepare-commit-msg.sample ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/bin/sh
#
# An example hook script to prepare the commit log message.
# Called by "git commit" with the name of the file that has the
# commit message, followed by the description of the commit
# message's source.  The hook's purpose is to edit the commit
# message file.  If the hook fails with a non-zero status,
# the commit is aborted.
#
# To enable this hook, rename this file to "prepare-commit-msg".

# This hook includes three examples. The first one removes the
# "# Please enter the commit message..." help message.
#
# The second includes the output of "git diff --name-status -r"
# into the message, just before the "git status" output.  It is
# commented because it doesn't cope with --amend or with squashed
# commits.
#
# The third example adds a Signed-off-by line to the message, that can
# still be edited.  This is rarely a good idea.

COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
SHA1=$3

/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE"

# case "$COMMIT_SOURCE,$SHA1" in
#  ,|template,)
#    /usr/bin/perl -i.bak -pe '
#       print "\n" . `git diff --cached --name-status -r`
# 	 if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;;
#  *) ;;
# esac

# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE"
# if test -z "$COMMIT_SOURCE"
# then
#   /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE"
# fi
push-to-checkout.sample ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#!/bin/sh

# An example hook script to update a checked-out tree on a git push.
#
# This hook is invoked by git-receive-pack(1) when it reacts to git
# push and updates reference(s) in its repository, and when the push
# tries to update the branch that is currently checked out and the
# receive.denyCurrentBranch configuration variable is set to
# updateInstead.
#
# By default, such a push is refused if the working tree and the index
# of the remote repository has any difference from the currently
# checked out commit; when both the working tree and the index match
# the current commit, they are updated to match the newly pushed tip
# of the branch. This hook is to be used to override the default
# behaviour; however the code below reimplements the default behaviour
# as a starting point for convenient modification.
#
# The hook receives the commit with which the tip of the current
# branch is going to be updated:
commit=$1

# It can exit with a non-zero status to refuse the push (when it does
# so, it must not modify the index or the working tree).
die () {
	echo >&2 "$*"
	exit 1
}

# Or it can make any necessary changes to the working tree and to the
# index to bring them to the desired state when the tip of the current
# branch is updated to the new commit, and exit with a zero status.
#
# For example, the hook can simply run git read-tree -u -m HEAD "$1"
# in order to emulate git fetch that is run in the reverse direction
# with git push, as the two-tree form of git read-tree -u -m is
# essentially the same as git switch or git checkout that switches
# branches while keeping the local changes in the working tree that do
# not interfere with the difference between the branches.

# The below is a more-or-less exact translation to shell of the C code
# for the default behaviour for git's push-to-checkout hook defined in
# the push_to_deploy() function in builtin/receive-pack.c.
#
# Note that the hook will be executed from the repository directory,
# not from the working tree, so if you want to perform operations on
# the working tree, you will have to adapt your code accordingly, e.g.
# by adding "cd .." or using relative paths.

if ! git update-index -q --ignore-submodules --refresh
then
	die "Up-to-date check failed"
fi

if ! git diff-files --quiet --ignore-submodules --
then
	die "Working directory has unstaged changes"
fi

# This is a rough translation of:
#
#   head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX
if git cat-file -e HEAD 2>/dev/null
then
	head=HEAD
else
	head=$(git hash-object -t tree --stdin </dev/null)
fi

if ! git diff-index --quiet --cached --ignore-submodules $head --
then
	die "Working directory has staged changes"
fi

if ! git read-tree -u -m "$commit"
then
	die "Could not update working tree to new HEAD"
fi
sendemail-validate.sample ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/bin/sh

# An example hook script to validate a patch (and/or patch series) before
# sending it via email.
#
# The hook should exit with non-zero status after issuing an appropriate
# message if it wants to prevent the email(s) from being sent.
#
# To enable this hook, rename this file to "sendemail-validate".
#
# By default, it will only check that the patch(es) can be applied on top of
# the default upstream branch without conflicts in a secondary worktree. After
# validation (successful or not) of the last patch of a series, the worktree
# will be deleted.
#
# The following config variables can be set to change the default remote and
# remote ref that are used to apply the patches against:
#
#   sendemail.validateRemote (default: origin)
#   sendemail.validateRemoteRef (default: HEAD)
#
# Replace the TODO placeholders with appropriate checks according to your
# needs.

validate_cover_letter () {
	file="$1"
	# TODO: Replace with appropriate checks (e.g. spell checking).
	true
}

validate_patch () {
	file="$1"
	# Ensure that the patch applies without conflicts.
	git am -3 "$file" || return
	# TODO: Replace with appropriate checks for this patch
	# (e.g. checkpatch.pl).
	true
}

validate_series () {
	# TODO: Replace with appropriate checks for the whole series
	# (e.g. quick build, coding style checks, etc.).
	true
}

# main -------------------------------------------------------------------------

if test "$GIT_SENDEMAIL_FILE_COUNTER" = 1
then
	remote=$(git config --default origin --get sendemail.validateRemote) &&
	ref=$(git config --default HEAD --get sendemail.validateRemoteRef) &&
	worktree=$(mktemp --tmpdir -d sendemail-validate.XXXXXXX) &&
	git worktree add -fd --checkout "$worktree" "refs/remotes/$remote/$ref" &&
	git config --replace-all sendemail.validateWorktree "$worktree"
else
	worktree=$(git config --get sendemail.validateWorktree)
fi || {
	echo "sendemail-validate: error: failed to prepare worktree" >&2
	exit 1
}

unset GIT_DIR GIT_WORK_TREE
cd "$worktree" &&

if grep -q "^diff --git " "$1"
then
	validate_patch "$1"
else
	validate_cover_letter "$1"
fi &&

if test "$GIT_SENDEMAIL_FILE_COUNTER" = "$GIT_SENDEMAIL_FILE_TOTAL"
then
	git config --unset-all sendemail.validateWorktree &&
	trap 'git worktree remove -ff "$worktree"' EXIT &&
	validate_series
fi
update.sample ASCII text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/bin/sh
#
# An example hook script to block unannotated tags from entering.
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
#
# To enable this hook, rename this file to "update".
#
# Config
# ------
# hooks.allowunannotated
#   This boolean sets whether unannotated tags will be allowed into the
#   repository.  By default they won't be.
# hooks.allowdeletetag
#   This boolean sets whether deleting tags will be allowed in the
#   repository.  By default they won't be.
# hooks.allowmodifytag
#   This boolean sets whether a tag may be modified after creation. By default
#   it won't be.
# hooks.allowdeletebranch
#   This boolean sets whether deleting branches will be allowed in the
#   repository.  By default they won't be.
# hooks.denycreatebranch
#   This boolean sets whether remotely creating branches will be denied
#   in the repository.  By default this is allowed.
#

# --- Command line
refname="$1"
oldrev="$2"
newrev="$3"

# --- Safety check
if [ -z "$GIT_DIR" ]; then
	echo "Don't run this script from the command line." >&2
	echo " (if you want, you could supply GIT_DIR then run" >&2
	echo "  $0 <ref> <oldrev> <newrev>)" >&2
	exit 1
fi

if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
	echo "usage: $0 <ref> <oldrev> <newrev>" >&2
	exit 1
fi

# --- Config
allowunannotated=$(git config --type=bool hooks.allowunannotated)
allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch)
denycreatebranch=$(git config --type=bool hooks.denycreatebranch)
allowdeletetag=$(git config --type=bool hooks.allowdeletetag)
allowmodifytag=$(git config --type=bool hooks.allowmodifytag)

# check for no description
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
case "$projectdesc" in
"Unnamed repository"* | "")
	echo "*** Project description file hasn't been set" >&2
	exit 1
	;;
esac

# --- Check types
# if $newrev is 0000...0000, it's a commit to delete a ref.
zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')
if [ "$newrev" = "$zero" ]; then
	newrev_type=delete
else
	newrev_type=$(git cat-file -t $newrev)
fi

case "$refname","$newrev_type" in
	refs/tags/*,commit)
		# un-annotated tag
		short_refname=${refname##refs/tags/}
		if [ "$allowunannotated" != "true" ]; then
			echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
			echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
			exit 1
		fi
		;;
	refs/tags/*,delete)
		# delete tag
		if [ "$allowdeletetag" != "true" ]; then
			echo "*** Deleting a tag is not allowed in this repository" >&2
			exit 1
		fi
		;;
	refs/tags/*,tag)
		# annotated tag
		if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
		then
			echo "*** Tag '$refname' already exists." >&2
			echo "*** Modifying a tag is not allowed in this repository." >&2
			exit 1
		fi
		;;
	refs/heads/*,commit)
		# branch
		if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
			echo "*** Creating a branch is not allowed in this repository" >&2
			exit 1
		fi
		;;
	refs/heads/*,delete)
		# delete branch
		if [ "$allowdeletebranch" != "true" ]; then
			echo "*** Deleting a branch is not allowed in this repository" >&2
			exit 1
		fi
		;;
	refs/remotes/*,commit)
		# tracking branch
		;;
	refs/remotes/*,delete)
		# delete tracking branch
		if [ "$allowdeletebranch" != "true" ]; then
			echo "*** Deleting a tracking branch is not allowed in this repository" >&2
			exit 1
		fi
		;;
	*)
		# Anything else (is there anything else?)
		echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
		exit 1
		;;
esac

# --- Finished
exit 0
dir info
exclude ASCII text
1
2
3
4
5
6
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
dir logs
dir refs
dir heads
master ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
0000000000000000000000000000000000000000 b193587fc2b7b0882d31f4b526e714b08a5006de DolphyWind <yunusaydin590@gmail.com> 1717846319 +1200	commit (initial): Initial commit
b193587fc2b7b0882d31f4b526e714b08a5006de 6659d371bb47f4afe82a997f7de09aef78d6089e DolphyWind <yunusaydin590@gmail.com> 1717850019 +1200	commit: Add gitignore
6659d371bb47f4afe82a997f7de09aef78d6089e be696def459a45122135536e906d647d86772026 DolphyWind <yunusaydin590@gmail.com> 1717850097 +1200	commit: Create grid trait and simple impl
be696def459a45122135536e906d647d86772026 35588af1fe35306264b7564e01d0ac6be8830c53 DolphyWind <yunusaydin590@gmail.com> 1717850121 +1200	commit (amend): Create grid trait and simple impl
35588af1fe35306264b7564e01d0ac6be8830c53 3a0b611d1f4f0d2df4ddbe5b49610903fe837c38 DolphyWind <yunusaydin590@gmail.com> 1717850809 +1200	commit: Make crate a library
3a0b611d1f4f0d2df4ddbe5b49610903fe837c38 3292ed208adf90aafeb3fa06be96de5db267543c DolphyWind <yunusaydin590@gmail.com> 1717850835 +1200	commit: Document ArrayGrid::new
3292ed208adf90aafeb3fa06be96de5db267543c 6e717d1ecacdf7d65dc8286b4bc2bae84196d937 DolphyWind <yunusaydin590@gmail.com> 1717851523 +1200	commit: Publicise and document
6e717d1ecacdf7d65dc8286b4bc2bae84196d937 02ab80302b798f7c77be67a7b48a80d9d0b6d272 DolphyWind <yunusaydin590@gmail.com> 1718303503 +1200	commit: Remove ArrayGrid
02ab80302b798f7c77be67a7b48a80d9d0b6d272 604d2bebbcb2d028c3db5557084c357f1962300a DolphyWind <yunusaydin590@gmail.com> 1718303515 +1200	commit: Implement algorithm
604d2bebbcb2d028c3db5557084c357f1962300a de69d5f9c9859b90411801ffe38421857794a7b3 DolphyWind <yunusaydin590@gmail.com> 1718306653 +1200	commit: Formatting
de69d5f9c9859b90411801ffe38421857794a7b3 08bffdc0431a3fffb8a0ddf25eaa3a0c9149bfc6 DolphyWind <yunusaydin590@gmail.com> 1718314280 +1200	commit: Fix vision cone when against a wall
08bffdc0431a3fffb8a0ddf25eaa3a0c9149bfc6 ada0c1a90f774c2f8f96da45b6542684ae1cef1a DolphyWind <yunusaydin590@gmail.com> 1718314441 +1200	commit: Remove lenience for targeting
ada0c1a90f774c2f8f96da45b6542684ae1cef1a d6f2e25da1a935b7416862e8728892e93d341f21 DolphyWind <yunusaydin590@gmail.com> 1718314456 +1200	commit: Add demonstration program
d6f2e25da1a935b7416862e8728892e93d341f21 51779086d648aacc65219179751b778d9326c7d7 DolphyWind <yunusaydin590@gmail.com> 1718314468 +1200	commit (amend): Add demonstration program
51779086d648aacc65219179751b778d9326c7d7 e5f9ff76148428dc88fb7c82c4d14052f406a1c7 DolphyWind <yunusaydin590@gmail.com> 1718315679 +1200	commit: Add README and model image
e5f9ff76148428dc88fb7c82c4d14052f406a1c7 c7729e0e8c5b5ee524c29940e66ee08a591c7f9b DolphyWind <yunusaydin590@gmail.com> 1718315891 +1200	commit: Formatting
c7729e0e8c5b5ee524c29940e66ee08a591c7f9b 876b2b9fa19a6042539487f068bd3a440dac56df DolphyWind <yunusaydin590@gmail.com> 1718332638 +1200	commit: Simplify handling of origin square vision
876b2b9fa19a6042539487f068bd3a440dac56df 99129412b67ec819c21baad5a51dfcf02becd31e DolphyWind <yunusaydin590@gmail.com> 1718333532 +1200	commit: Fix ability to see slightly through diagonals
HEAD ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
0000000000000000000000000000000000000000 b193587fc2b7b0882d31f4b526e714b08a5006de DolphyWind <yunusaydin590@gmail.com> 1717846319 +1200	commit (initial): Initial commit
b193587fc2b7b0882d31f4b526e714b08a5006de 6659d371bb47f4afe82a997f7de09aef78d6089e DolphyWind <yunusaydin590@gmail.com> 1717850019 +1200	commit: Add gitignore
6659d371bb47f4afe82a997f7de09aef78d6089e be696def459a45122135536e906d647d86772026 DolphyWind <yunusaydin590@gmail.com> 1717850097 +1200	commit: Create grid trait and simple impl
be696def459a45122135536e906d647d86772026 35588af1fe35306264b7564e01d0ac6be8830c53 DolphyWind <yunusaydin590@gmail.com> 1717850121 +1200	commit (amend): Create grid trait and simple impl
35588af1fe35306264b7564e01d0ac6be8830c53 3a0b611d1f4f0d2df4ddbe5b49610903fe837c38 DolphyWind <yunusaydin590@gmail.com> 1717850809 +1200	commit: Make crate a library
3a0b611d1f4f0d2df4ddbe5b49610903fe837c38 3292ed208adf90aafeb3fa06be96de5db267543c DolphyWind <yunusaydin590@gmail.com> 1717850835 +1200	commit: Document ArrayGrid::new
3292ed208adf90aafeb3fa06be96de5db267543c 6e717d1ecacdf7d65dc8286b4bc2bae84196d937 DolphyWind <yunusaydin590@gmail.com> 1717851523 +1200	commit: Publicise and document
6e717d1ecacdf7d65dc8286b4bc2bae84196d937 02ab80302b798f7c77be67a7b48a80d9d0b6d272 DolphyWind <yunusaydin590@gmail.com> 1718303503 +1200	commit: Remove ArrayGrid
02ab80302b798f7c77be67a7b48a80d9d0b6d272 604d2bebbcb2d028c3db5557084c357f1962300a DolphyWind <yunusaydin590@gmail.com> 1718303515 +1200	commit: Implement algorithm
604d2bebbcb2d028c3db5557084c357f1962300a de69d5f9c9859b90411801ffe38421857794a7b3 DolphyWind <yunusaydin590@gmail.com> 1718306653 +1200	commit: Formatting
de69d5f9c9859b90411801ffe38421857794a7b3 08bffdc0431a3fffb8a0ddf25eaa3a0c9149bfc6 DolphyWind <yunusaydin590@gmail.com> 1718314280 +1200	commit: Fix vision cone when against a wall
08bffdc0431a3fffb8a0ddf25eaa3a0c9149bfc6 ada0c1a90f774c2f8f96da45b6542684ae1cef1a DolphyWind <yunusaydin590@gmail.com> 1718314441 +1200	commit: Remove lenience for targeting
ada0c1a90f774c2f8f96da45b6542684ae1cef1a d6f2e25da1a935b7416862e8728892e93d341f21 DolphyWind <yunusaydin590@gmail.com> 1718314456 +1200	commit: Add demonstration program
d6f2e25da1a935b7416862e8728892e93d341f21 51779086d648aacc65219179751b778d9326c7d7 DolphyWind <yunusaydin590@gmail.com> 1718314468 +1200	commit (amend): Add demonstration program
51779086d648aacc65219179751b778d9326c7d7 e5f9ff76148428dc88fb7c82c4d14052f406a1c7 DolphyWind <yunusaydin590@gmail.com> 1718315679 +1200	commit: Add README and model image
e5f9ff76148428dc88fb7c82c4d14052f406a1c7 c7729e0e8c5b5ee524c29940e66ee08a591c7f9b DolphyWind <yunusaydin590@gmail.com> 1718315891 +1200	commit: Formatting
c7729e0e8c5b5ee524c29940e66ee08a591c7f9b 876b2b9fa19a6042539487f068bd3a440dac56df DolphyWind <yunusaydin590@gmail.com> 1718332638 +1200	commit: Simplify handling of origin square vision
876b2b9fa19a6042539487f068bd3a440dac56df 99129412b67ec819c21baad5a51dfcf02becd31e DolphyWind <yunusaydin590@gmail.com> 1718333532 +1200	commit: Fix ability to see slightly through diagonals
dir objects
dir 01
2fe745d58fa5f8c7c5a30cebfc99715d9e784f zlib compressed data
1
cg: couldn't decode file contents
dir 02
ab80302b798f7c77be67a7b48a80d9d0b6d272 zlib compressed data
1
cg: couldn't decode file contents
dir 03
ad6ebc8688e96c20bfa7da4eb741516018be69 zlib compressed data
1
cg: couldn't decode file contents
dir 08
bffdc0431a3fffb8a0ddf25eaa3a0c9149bfc6 zlib compressed data
1
cg: couldn't decode file contents
dir 12
087f106f3fa951e3a65e3f360578848f34448f zlib compressed data
1
cg: couldn't decode file contents
dir 14
65c359bbef8b29d5f806537d02335d60047a5b zlib compressed data
1
cg: couldn't decode file contents
dir 1a
66285709b4077981870eb44aaf3c09a844b400 zlib compressed data
1
cg: couldn't decode file contents
dir 24
3869209e94ae184b94f3e1cdb509e0cbd413e2 zlib compressed data
1
cg: couldn't decode file contents
dir 27
5d4d78a3076a17294f1e6c184852e130bbdf40 zlib compressed data
1
cg: couldn't decode file contents
dir 29
96d22d8599507c194c8dd5ab67fb92974c84c2 zlib compressed data
1
cg: couldn't decode file contents
dir 30
3765e0760a85661609553c672c56118617cb08 zlib compressed data
1
cg: couldn't decode file contents
5157a396c6858705a9cb625bab219053264ee4 zlib compressed data
1
cg: couldn't decode file contents
dir 32
92ed208adf90aafeb3fa06be96de5db267543c zlib compressed data
1
cg: couldn't decode file contents
dir 33
568832d5f28717afe278fbbcbf91b1e467b0a6 zlib compressed data
1
cg: couldn't decode file contents
dir 35
588af1fe35306264b7564e01d0ac6be8830c53 zlib compressed data
1
cg: couldn't decode file contents
dir 3a
0b611d1f4f0d2df4ddbe5b49610903fe837c38 zlib compressed data
1
cg: couldn't decode file contents
dir 42
0d7e28f549eec7b568c894b3938e656ff8617a zlib compressed data
1
cg: couldn't decode file contents
dir 44
5d84a31f5a15fc1dcb5eb3485d2cb12e92a1d7 zlib compressed data
1
cg: couldn't decode file contents
dir 50
0f0206f30afbe052d452a79aefac6b8437e2be zlib compressed data
1
cg: couldn't decode file contents
dir 51
779086d648aacc65219179751b778d9326c7d7 zlib compressed data
1
cg: couldn't decode file contents
dir 52
652fb92bf07daad0a35ee8c447e102ced75ff7 zlib compressed data
1
cg: couldn't decode file contents
dir 55
bf273a2907757a7d72b97ae244c08e7e4ef48d zlib compressed data
1
cg: couldn't decode file contents
dir 5c
32c6d7a19d3e8af1ca15ed73747646c1f1b17f zlib compressed data
1
cg: couldn't decode file contents
dir 5d
6ea8f7abf881bccdb29deb42d96fe33d5edab8 zlib compressed data
1
cg: couldn't decode file contents
dir 60
4d2bebbcb2d028c3db5557084c357f1962300a zlib compressed data
1
cg: couldn't decode file contents
dir 64
713ade624b6993d2e81eb4e90851c9dc8cc75d zlib compressed data
1
cg: couldn't decode file contents
dir 66
59d371bb47f4afe82a997f7de09aef78d6089e zlib compressed data
1
cg: couldn't decode file contents
dir 68
e56d3ce5e2de5318338662712c650743e0033d zlib compressed data
1
cg: couldn't decode file contents
dir 6e
717d1ecacdf7d65dc8286b4bc2bae84196d937 zlib compressed data
1
cg: couldn't decode file contents
dir 73
1509c68c046ecfa4df853c63317046795f26ca zlib compressed data
1
cg: couldn't decode file contents
dir 78
ee5262b1662f33994c56b353e73f09ab482dd1 zlib compressed data
1
cg: couldn't decode file contents
dir 7c
ae1fbcf3e390cfb5a4408b31948101331b6f47 zlib compressed data
1
cg: couldn't decode file contents
dir 7d
40bcefc2ae308fcf9134f4ec94bfc259bdf65f zlib compressed data
1
cg: couldn't decode file contents
dd12423e443215c76b5bc20ee8e92507423cb1 zlib compressed data
1
cg: couldn't decode file contents
dir 7e
96943d88b06edc986485ebd77819181725eed0 zlib compressed data
1
cg: couldn't decode file contents
dir 80
6dd7ede7fbebe3cf3e300b1b65ea9ce1e0dc3b zlib compressed data
1
cg: couldn't decode file contents
dir 81
f0b18c0a088e3ed508e2c1e91d5d39b3cb0eea zlib compressed data
1
cg: couldn't decode file contents
dir 85
25b85ce90200a913cb23a0e0a4dbb6a3c74387 zlib compressed data
1
cg: couldn't decode file contents
dir 86
b60e0168f16be602d9507c820ecfb57b699c80 zlib compressed data
1
cg: couldn't decode file contents
dir 87
6b2b9fa19a6042539487f068bd3a440dac56df zlib compressed data
1
cg: couldn't decode file contents
dir 88
80b259a960b2937691140dd3b9985d598bbc76 zlib compressed data
1
cg: couldn't decode file contents
dir 8e
e9c916c23e096d0db11962f079900b414126a8 zlib compressed data
1
cg: couldn't decode file contents
dir 90
cf223f8b77e5849a96a6c6a094ab526b1740bf zlib compressed data
1
cg: couldn't decode file contents
dir 96
ef6c0b944e24fc22f51f18136cd62ffd5b0b8f zlib compressed data
1
cg: couldn't decode file contents
dir 99
000fea5a801de01941176fad53d563eae213f5 zlib compressed data
1
cg: couldn't decode file contents
129412b67ec819c21baad5a51dfcf02becd31e zlib compressed data
1
cg: couldn't decode file contents
dir 9c
30ca322c63a9c1ac943020390099d6ec50de5c zlib compressed data
1
cg: couldn't decode file contents
dir 9e
62aaad2aa7e65f182ac6b69641985bc04924ed zlib compressed data
1
cg: couldn't decode file contents
72dfe706643142f5e285c066a4e454aa800ef2 zlib compressed data
1
cg: couldn't decode file contents
dir a0
05c1e0741ace9613f10add316670f3a9534af2 zlib compressed data
1
cg: couldn't decode file contents
dir a4
02001d1eac62cd3b6b586eb48fe0e75f3593bd zlib compressed data
1
cg: couldn't decode file contents
dir ad
a0c1a90f774c2f8f96da45b6542684ae1cef1a zlib compressed data
1
cg: couldn't decode file contents
dir b0
f597c617282abc293a948eb8ea62df14d7cf30 zlib compressed data
1
cg: couldn't decode file contents
dir b1
93587fc2b7b0882d31f4b526e714b08a5006de zlib compressed data
1
cg: couldn't decode file contents
dir b6
bde3241a7bff463d13484544943552c18cbdbd zlib compressed data
1
cg: couldn't decode file contents
dir be
696def459a45122135536e906d647d86772026 zlib compressed data
1
cg: couldn't decode file contents
dir c0
ffd6099ef1e5bae55d843891b3daae95deb6c2 zlib compressed data
1
cg: couldn't decode file contents
dir c7
729e0e8c5b5ee524c29940e66ee08a591c7f9b zlib compressed data
1
cg: couldn't decode file contents
dir d6
f2e25da1a935b7416862e8728892e93d341f21 zlib compressed data
1
cg: couldn't decode file contents
dir d7
c0e1d11538114fd1214cce499d16f2fc18d354 zlib compressed data
1
cg: couldn't decode file contents
dir de
69d5f9c9859b90411801ffe38421857794a7b3 zlib compressed data
1
cg: couldn't decode file contents
dir e1
685565cd45663d44b27c65bad8e8d1e466feec zlib compressed data
1
cg: couldn't decode file contents
dir e2
1441333aa6124abfc4b37f534b6fa5cff37e07 zlib compressed data
1
cg: couldn't decode file contents
dir e5
f9ff76148428dc88fb7c82c4d14052f406a1c7 zlib compressed data
1
cg: couldn't decode file contents
dir e7
a11a969c037e00a796aafeff6258501ec15e9a zlib compressed data
1
cg: couldn't decode file contents
dir ec
055c3bb920eeeedccfc3296fadce79c2a61406 zlib compressed data
1
码䯊쥏到㑠죍佑䠯쩌뇦Ȁ㕪լ
08560d7403934d30ee56a4a1cb72084dcdc66e zlib compressed data
1
cg: couldn't decode file contents
d909f554fe8e4a77337fe8eb1d316e9a321ede zlib compressed data
1
cg: couldn't decode file contents
dir ed
b93443dd40b2ac4c24f240955c0a5165a0004e zlib compressed data
1
cg: couldn't decode file contents
dir ee
3755f28b4f4e9352815ae4b94aa2c88fd9acea zlib compressed data
1
cg: couldn't decode file contents
dir f2
6c0179bc84d8b0f49b8355084e7474e9e4792e zlib compressed data
1
cg: couldn't decode file contents
dir f3
1d225c150873f70847a1042fd7486a59602888 zlib compressed data
1
cg: couldn't decode file contents
dir f4
24aaf940684282c7ffc64c02e769e7edc37bad zlib compressed data
1
cg: couldn't decode file contents
dir f7
a1b46d277d4650c443e92631f5e09ed5c21b10 zlib compressed data
1
码䯊쥏到㑡⠨䵒죍佑䠯쩌뇦Ȁ倷ۗ
dir fa
1ce19e75fffba85896c90c85c1db594b75a645 zlib compressed data
1
cg: couldn't decode file contents
dir refs
dir heads
master ASCII text
1
99129412b67ec819c21baad5a51dfcf02becd31e
COMMIT_EDITMSG ASCII text
1
Fix ability to see slightly through diagonals
HEAD ASCII text
1
ref: refs/heads/master
config ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
[user]
	name = DolphyWind
	email = yunusaydin590@gmail.com
[commit]
	gpgsign = false
description ASCII text
1
Unnamed repository; edit this file 'description' to name the repository.
index Git index, version 2, 8 entries
1
cg: couldn't decode file contents
dir examples
demo.rs ASCII text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
use floss::{mark_vision, Grid, Pos};
use std::io::{stdin, stdout, Write};
use termion::{
    color,
    cursor::{self, HideCursor},
    event::{Event, Key, MouseButton, MouseEvent},
    input::{MouseTerminal, TermRead},
    raw::IntoRawMode,
    screen::IntoAlternateScreen,
    style,
};

pub struct Field {
    walls: Vec<bool>,
    visible: Vec<bool>,
    width: usize,
    height: usize,
    x: usize,
    y: usize,
}

impl Grid for Field {
    fn is_wall(&self, p: Pos) -> bool {
        if let Some(i) = self.pos_index(p) {
            self.walls[i]
        } else {
            true
        }
    }

    fn mark_visible(&mut self, p: Pos) {
        if let Some(i) = self.pos_index(p) {
            self.visible[i] = true;
        }
    }
}

impl Field {
    fn new((width, height): (u16, u16)) -> Self {
        let width = width as usize / 2;
        let height = height as usize;
        let size = width * height;
        Self {
            walls: vec![false; size],
            visible: vec![false; size],
            width,
            height,
            x: width / 2,
            y: height / 2,
        }
    }

    fn pos_index(&self, (x, y): Pos) -> Option<usize> {
        if x < 0 || y < 0 {
            return None;
        }
        let x = x as usize;
        let y = y as usize;
        if x < self.width && y < self.height {
            Some(y * self.width + x)
        } else {
            None
        }
    }

    fn mouse_pos_index(&self, x: u16, y: u16) -> Option<usize> {
        self.pos_index(((x as isize - 1) / 2, y as isize - 1))
    }

    fn draw(&self) {
        let player = self.y * self.width + self.x;

        print!(
            "{}{}",
            color::Bg(color::AnsiValue::grayscale(1)),
            cursor::Goto(1, 1)
        );
        for i in 0..self.walls.len() {
            if self.visible[i] {
                print!("{}{}", color::Fg(color::LightWhite), style::Bold);
            } else {
                print!(
                    "{}{}",
                    color::Fg(color::AnsiValue::grayscale(2)),
                    style::NoBold
                );
            }

            if i == player {
                print!("@ ");
            } else if self.walls[i] {
                print!("# ");
            } else {
                print!(". ");
            }

            if (i + 1) % self.width == 0 && i < self.walls.len() - 1 {
                println!("\r");
            }
        }

        stdout().flush().unwrap();
    }

    fn render(&mut self) {
        for x in self.visible.iter_mut() {
            *x = false;
        }
        mark_vision(self, (self.x as isize, self.y as isize));
        self.draw();
    }

    fn shift(&mut self, x: isize, y: isize) {
        let new_x = self.x as isize + x;
        let new_y = self.y as isize + y;
        if !self.is_wall((new_x, new_y)) {
            self.x = new_x as usize;
            self.y = new_y as usize;
        }
    }
}

fn main() {
    let _screen = HideCursor::from(MouseTerminal::from(
        stdout()
            .into_raw_mode()
            .unwrap()
            .into_alternate_screen()
            .unwrap(),
    ));

    let mut field = Field::new(termion::terminal_size().unwrap());
    field.render();

    let mut mouse_setting = None;

    for event in stdin().events() {
        match event.unwrap() {
            Event::Key(Key::Ctrl('c')) => break,
            Event::Key(Key::Esc) => break,
            Event::Key(Key::Char('w')) => field.shift(0, -1),
            Event::Key(Key::Char('a')) => field.shift(-1, 0),
            Event::Key(Key::Char('s')) => field.shift(0, 1),
            Event::Key(Key::Char('d')) => field.shift(1, 0),
            Event::Mouse(MouseEvent::Press(MouseButton::Left, x, y)) => {
                let Some(i) = field.mouse_pos_index(x, y) else {
                    continue;
                };
                let new = !field.walls[i];
                mouse_setting = Some(new);
                field.walls[i] = new;
            }
            Event::Mouse(MouseEvent::Release(_, _)) => mouse_setting = None,
            Event::Mouse(MouseEvent::Hold(x, y)) => {
                let Some(i) = field.mouse_pos_index(x, y) else {
                    continue;
                };
                let Some(x) = mouse_setting else { continue };
                field.walls[i] = x;
            }
            _ => {}
        }
        field.render();
    }
}
dir src
grid.rs ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/// The type of an absolute point on a grid.
/// The actual origin point of these coordinates is irrelevant as long as the [`Grid`] instance treats them
/// consistently as Cartesian coordinates. For this reason, [`isize`] is used over [`usize`] for convenience
/// in the case that your use case involves negative coordinates.
pub type Pos = (isize, isize);

/// Inspection of the contents of a grid for the purposes of vision calculation.
/// Types implementing `Grid` represent a Cartesian grid of cells, each of which either blocks
/// vision ("is a wall") or does not.
pub trait Grid {
    /// Returns whether the provided position should be considered opaque.
    /// This method should return `true` for positions that are out of bounds to prevent infinite loops.
    fn is_wall(&self, pos: Pos) -> bool;

    /// This method is called when a certain position has been found to be reachable.
    /// Note that the library does not keep track of the size of the grid, and thus
    /// you may have to do bounds checking to explicitly ignore out-of-bounds positions.
    fn mark_visible(&mut self, pos: Pos);
}
lib.rs ASCII text
1
2
3
4
5
mod grid;
mod vision;

pub use grid::{Grid, Pos};
pub use vision::mark_vision;
vision.rs ASCII text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
use crate::grid::{Grid, Pos};

// This algorithm is easier to implement when done in octants, and the steps are exactly the same for each octant.
// This struct translates coordinates so that the code for one octant can be used on all of them.
struct Octant<'a, T> {
    grid: &'a mut T,
    origin: Pos,
    transform: (isize, isize, isize, isize),
}

impl<'a, T> Octant<'a, T> {
    fn new(grid: &'a mut T, origin: Pos) -> Self {
        Self {
            grid,
            origin,
            transform: (1, 0, 0, 1),
        }
    }

    /// Rotate the internal transform by 90 degrees.
    fn rot(&mut self) {
        let (a, b, c, d) = self.transform;
        self.transform = (-c, -d, a, b);
    }

    /// Flip the internal transform horizontally.
    fn flip(&mut self) {
        let (a, b, c, d) = self.transform;
        self.transform = (-a, -b, c, d);
    }

    fn transform(&self, x: isize, y: isize) -> Pos {
        let (a, b, c, d) = self.transform;

        (self.origin.0 + a * x + b * y, self.origin.1 + c * x + d * y)
    }
}

impl<T: Grid> Octant<'_, T> {
    fn is_wall(&self, x: isize, y: isize) -> bool {
        self.grid.is_wall(self.transform(x, y))
    }

    fn mark_visible(&mut self, x: isize, y: isize) {
        self.grid.mark_visible(self.transform(x, y))
    }
}

// x ------>
//
// -------------- <- top (slope)
// \
//  \                       y
//   \                      |
//    \                     |
//     \                    |
//      \                   v
//       \
//        \
//         \
//          \
//           \
//            \
//             \
//              \ <- bottom (slope)

// upper_scope and lower_slope find the upper and lower bounds of slope required to "hit"
// a given square, taking into account bevelling.

//       possible upper slopes
//           /---------\
//           v         v
// /---------^---------\
// |        / \        |
// |       /   \       |
// |      /     \      |
// |     /       \     |
// |    /         \    |
// |   /           \   |
// |  /             \  |
// | /               \ |
// |/                 \|
// |\                 /|
// | \               / |
// |  \             /  |
// |   \           /   |
// |    \         /    |
// |     \       /     |
// |      \     /      |
// |       \   /       |
// |        \ /        |
// \---------v---------/
fn upper_slope<T: Grid>(oct: &Octant<T>, x: isize, y: isize) -> f64 {
    let mut a = x as f64;
    let b = y as f64 - 0.5;
    if oct.is_wall(x + 1, y) || oct.is_wall(x, y - 1) || oct.is_wall(x + 1, y - 1) {
        a += 0.5;
    }
    b / a
}

//     /---------^---------\
//     |        / \        |
//     |       /   \       |
//     |      /     \      |
//     |     /       \     |
//     |    /         \    |
//     |   /           \   |
//     |  /             \  |
//     | /               \ |
//     |/                 \|
//     |\                 /|
//     | \               / |
//     |  \             /  |
//     |   \           /   |
//     |    \         /    |
//     |     \       /     |
//     |      \     /      |
//     |       \   /       |
//     |        \ /        |
//     \---------v---------/
//     ^         ^
//     \---------/
// possible lower slopes
fn lower_slope<T: Grid>(oct: &Octant<T>, x: isize, y: isize) -> f64 {
    let mut a = x as f64;
    let b = y as f64 + 0.5;
    if oct.is_wall(x - 1, y) || oct.is_wall(x, y + 1) || oct.is_wall(x - 1, y + 1) {
        a -= 0.5;
    }
    b / a
}

fn hitting<T: Grid>(oct: &Octant<T>, x: isize, y: isize, slope: f64) -> bool {
    oct.is_wall(x, y) && upper_slope(oct, x, y) < slope && slope < lower_slope(oct, x, y)
}

fn cast<T: Grid>(oct: &mut Octant<T>, x: isize, top: f64, bottom: f64) {
    let mut start = (top * x as f64).ceil() as isize;
    let mut end = (bottom * x as f64).floor() as isize;

    if hitting(oct, x, start - 1, top) {
        start -= 1;
    }
    if hitting(oct, x, end + 1, bottom) {
        end += 1;
    }

    let mut next_top = top;
    let mut last_was_wall = oct.is_wall(x, start);
    oct.mark_visible(x, start);

    for y in start + 1..=end {
        let this_is_wall = oct.is_wall(x, y);

        if last_was_wall && !this_is_wall {
            next_top = lower_slope(oct, x, y - 1);
        } else if !last_was_wall && this_is_wall {
            cast(oct, x + 1, next_top, upper_slope(oct, x, y));
        }

        if next_top == bottom {
            break;
        }
        oct.mark_visible(x, y);

        last_was_wall = this_is_wall;
    }

    if !last_was_wall {
        cast(oct, x + 1, next_top, bottom);
    }
}

/// Mark the visible points on a grid.
///
/// Calls [`Grid::mark_visible`] for each cell in `grid` visible from `origin`, including walls, non-walls, and the origin itself.
pub fn mark_vision<T: Grid>(grid: &mut T, origin: Pos) {
    grid.mark_visible(origin);
    let mut oct = Octant::new(grid, origin);
    for _ in 0..2 {
        for _ in 0..4 {
            cast(&mut oct, 1, 0.0, 1.0);
            oct.rot();
        }
        oct.flip();
    }
}
.gitignore ASCII text
1
2
/target
Cargo.lock
Cargo.toml ASCII text
1
2
3
4
5
6
7
[package]
name = "floss"
version = "0.1.0"
edition = "2021"

[dev-dependencies]
termion = "4.0.2"
README.md ASCII text
1
2
3
4
5
6
7
8
9
# floss, the LOS engine in Rust

floss implements blazingly fast, precise and beautiful line-of-sight tracking on a grid in Rust, ideal for traditional roguelikes and the like.
The library is fully documented, so you can jump right in!

For an example of what this library does, run the example (`cargo run --release --example demo`). Use WASD to move around and left click to place or remove walls.
`Esc` or `Ctrl-C` quits.

Please note that the library supports no customization of the algorithm of any kind. What you get is what you get.
model.png PNG image data, 600 x 320, 8-bit/color RGBA, non-interlaced

round #59

submitted at
1 like

guesses
comments 0

post a comment


entry.jq ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#!/usr/bin/env -S jq -Rrf

def parse: [
  scan("(?<r>[#+@!?]|\\[((?&r)*)\\|((?&r)*)])") |
  if .[1] != null then {cond: .[1] | parse, body: .[2] | parse}
  else .[0]
  end
];

def new_state: {
    stack: [],
    memory: {},
};

def ensure_height($height):
  .stack |= [range([$height - (. | length), 0] | max) | 0] + .
;

def gcd($a; $b): if $b == 0 then $a else gcd($b; $a % $b) end;
def lcm($a; $b): $a * $b / gcd($a; $b);
def lcm: reduce .[] as $n (1; lcm(.; $n));

def probpipe(x; y):
  [x | [y]] | (map(length) | lcm) as $l |
  .[] as $p | range($l / ($p | length)) | $p[]
;

def for($vals; filter):
  if ($vals | length) == 0 then .
  else {val: $vals[0], state: .} | filter | for(vals[1:]; filter)
  end
;

def eval($code):
  def do_loop($bound; $loop): probpipe(
    eval($loop.cond);
    ensure_height(1) | .stack[-1] as $cond | del(.stack[-1]) |
    if $bound != 0 and $cond != 0 then
      eval($loop.body) | do_loop($bound - 1; $loop)
    end
  );

  for($code; . as {$val, $state} | $state |
    if $val == "#" then
      .stack += [0]
    elif $val == "+" then
      ensure_height(1) | .stack[-1] += 1
    elif $val == "@" then
      ensure_height(1) | .stack[-1] = .memory[.stack[-1] | tostring]
    elif $val == "!" then
      ensure_height(2) |
      .memory[.stack[-1] | tostring] = .stack[-2] |
      del(.stack[-1, -2])
    elif $val == "?" then
      ensure_height(1) |
      range(.stack[-1] + 1) as $n | .stack[-1] = $n
    else
      ensure_height(1) |
      .stack[-1] as $bound | del(.stack[-1]) | do_loop($bound; $val)
    end
  )
;

def run:
  parse as $code | new_state | eval($code) |
  ensure_height(1).stack[-1] | tostring
;

def most_frequent:
  reduce .[] as $k ({}; .[$k] += 1) | to_entries | max_by(.value).key
;

[run] | most_frequent

round #58

submitted at
2 likes

guesses
comments 1
Nova

󠇭󠄧󠅪󠅈󠅊󠁰󠁰󠁴󠇖󠇆󠆤󠄶󠁲󠁰󠄑󠁱󠄆󠁰󠁰󠁰󠅤󠄟󠇕󠆓󠁱󠁰󠁰󠆰󠁰󠁰󠁰󠁰󠆪󠆧󠄓󠅈󠄜󠇍󠅧󠆢󠁰󠁱󠄉󠁱󠆕󠄜󠅱󠆼󠄏󠆦󠇣󠅭󠁱󠁰󠁰󠁰󠁰󠁴󠅉󠅊


post a comment


README.md ASCII text
1
2
3
4
sorry this was meant to be more fully featured but i couldnt be bothered
run client.py
if something is wrong use wrong.py, please don't abuse this it clears all game state for everyone
only supports one game being played at a time
cg.py ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import constants
import serialize
import requests
from bs4 import BeautifulSoup

def find_comment():
    soup = BeautifulSoup(requests.get(constants.URL).text, features="lxml")
    for s in soup.find_all(class_="comment"):
        if str(s.strong) != f'<strong>{constants.NAME} <span class="verified"></span></strong>':
            continue
        try:
            serialize.deserialize(s.p.text)
        except ValueError:
            continue
        return s

def load():
    if comment := find_comment():
        return serialize.deserialize(comment.p.text)

def dump(data):
    c = {"type": "comment", "parent": constants.ID, "persona": "-1", "content": serialize.serialize(data)}
    if comment := find_comment():
        c["edit"] = comment.a["href"][2:]
    requests.post(constants.URL, cookies={"session": constants.COOKIE}, data=c)
client.py ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import cg
import game
import time

data = cg.load()
try:
    g = game.UltimateTicTacToe.load(data)
    we_are = game.Y
except Exception:
    g = game.UltimateTicTacToe()
    we_are = game.X
    data = g.dump()
    cg.dump(data)

if not g.is_virgin():
    print("game ongoing: entering spectator mode")
    while True:
        time.sleep(3)
        new = cg.load()
        if new is None:
            print("game ended")
            break
        if new != data:
            data = new
            print("\033[2J", end="")
            try:
                print(game.UltimateTicTacToe.load(data).render(None))
            except Exception:
                pass

SPACES = [(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)]

def draw_board(g, selected):
    print("\033[2J" + "\n"*1000, end="")
    print("you are", "XO"[we_are-1])
    print(g.render(selected))

while True:
    draw_board(g, None)
    if g.to_play != we_are:
        print("waiting for enemy move")
        time.sleep(3)
        d = cg.load()
        if d is None:
            print("you lost")
            break
        g = game.UltimateTicTacToe.load(d)
        continue
    selected_space = g.next_space
    draw_board(g, selected_space)
    while True:
        if selected_space:
            if not g.next_space:
                print("you may do j to pick a different subboard or 1-9 to draw in a cell")
            else:
                print(selected_space, g.next_space)
                print("do 1-9 to draw in a cell")
        else:
            print("do 1-9 to pick a subboard")
        inp = input("> ")
        if inp == "j" and not g.next_space and selected_space:
            selected_space = None
            draw_board(g, selected_space)
        elif inp.isdigit() and 1 <= int(inp) <= 9:
            space = SPACES[int(inp)-1]
            if not selected_space:
                selected_space = space
                draw_board(g, selected_space)
            else:
                if g.make_move(*selected_space, *space):
                    print("you won")
                    exit(0)
                cg.dump(g.dump())
                break
        else:
            print("you may not do that")
            continue
constants.py ASCII text, with very long lines (501)
1
2
3
4
URL = "https://cg.esolangs.gay/58/"
NAME = "Nova"
ID = "10"
COOKIE = ".eJxdT8tugkAU_ZdZGzMDDAI7BYqjUZCXSNMQhKFORN6tgvHfi13UpKt7c889rzvQiKOathaZc89dcpHjzl0dKID2q9PRSJjJVsQbCNoy0pLCxolKRHKuAl9dydPxKQv4LTwEdj7O_MCv8vRNepJ4e5gPYRDiZI9O8T5nG8NXvb1fUf0k0qU_pG6Yb1HCst20xGX-GaJZVZGqX6sc03BSf_mXMl7yh2990Wtep3laULASTP4Hds21vgXKHcRJQts26sozLcYCeFyu4dWYdzv79s0xNfYsJ750upQWQj3q0FvFGtpGcQcUNEMi5CEU5SmaCZwoCy-cjWoiFCQIJ6Ch2Xg6_ZlkUm2dC8v0DF9YVsyw2CWvU8xv3GO9z0aTNikrCpR3wFJadCzrwccE_LKjrn8iYEHjhjbg8SrmObodEQ0oMyggGSMRy5IERcRB_PgBOsqJqA.Zj9P9Q.BfBHLmn88dmSS-FN_PHeBSTb9Cw"
game.py SoftQuad troff Context intermediate
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
X = 1
Y = 2
SIZE = 3
ICONS = " XO"
ICON_SELECTED = "*"
VERT_LINE = "|"
HORI_LINE = "ー"
CROSS = "+"

class CountResult:
    def __init__(self, player, v):
        self.player = player
        self.v = v

    def then(self, c, i, p=True):
        if self.v or not p:
            return self
        return c.hit(self.player, i)

class Counts:
    def __init__(self, s):
        self.v = ([0]*s, [0]*s)

    def hit(self, player, i):
        self.v[player-1][i] += 1
        return CountResult(player, self.v[player-1][i] == SIZE)

    def dump(self):
        return self.v

    @classmethod
    def load(cls, d):
        me = cls(0)
        me.v = d
        return me

class TicTacToe:
    def __init__(self):
        self.board = [[0]*SIZE for _ in range(SIZE)]
        self.pipe_counts = Counts(SIZE)
        self.dash_counts = Counts(SIZE)
        self.reverse_solidus_counts = Counts(1)
        self.solidus_counts = Counts(1)

    def make_move(self, player, x, y):
        if not (0 <= x < SIZE and 0 <= y < SIZE):
            raise ValueError("invalid coordinates")
        if self.board[y][x]:
            raise ValueError("position not empty")

        self.board[y][x] = player
        # what a moment
        return (
            self.pipe_counts.hit(player, x)
            .then(self.dash_counts, y)
            .then(self.reverse_solidus_counts, 0, x == y)
            .then(self.solidus_counts, 0, SIZE-1-x == y)
        ).v

    def dump(self):
        return {
            "board": self.board,
            "pipe_counts": self.pipe_counts.dump(),
            "dash_counts": self.dash_counts.dump(),
            "reverse_solidus_counts": self.reverse_solidus_counts.dump(),
            "solidus_counts": self.solidus_counts.dump(),
        }

    @classmethod
    def load(cls, d):
        me = cls()
        me.board = d["board"]
        me.pipe_counts = Counts.load(d["pipe_counts"])
        me.dash_counts = Counts.load(d["dash_counts"])
        me.reverse_solidus_counts = Counts.load(d["reverse_solidus_counts"])
        me.solidus_counts = Counts.load(d["solidus_counts"])
        return me

class UltimateTicTacToe:
    def __init__(self):
        # this board has the winning state of the smaller boards. when it's won the game is over
        self.overall = TicTacToe()
        self.innards = [[TicTacToe() for _ in range(SIZE)] for _ in range(SIZE)]
        self.to_play = X
        self.next_space = None

    def is_virgin(self):
        for sy in self.innards:
            for sx in sy:
                for y in sx.board:
                    for x in y:
                        if x == Y:
                            return False
        return True

    def make_move(self, x, y, sx, sy):
        if not (0 <= x < SIZE and 0 <= y < SIZE):
            raise ValueError("invalid coordinates")
        if self.next_space and (x, y) != self.next_space:
            raise ValueError("sub-board not active")
        if self.overall.board[y][x]:
            raise ValueError("sub-board already completed")
        won = self.innards[y][x].make_move(self.to_play, sx, sy)
        try:
            return won and self.overall.make_move(self.to_play, x, y)
        finally:
            self.next_space = None if self.overall.board[sy][sx] else (sx, sy)
            self.to_play = Y if self.to_play == X else X

    def render(self, select):
        outside = ""
        out = ""
        # ok, here we go
        for sy in range(SIZE):
            for y in range(SIZE):
                for sx in range(SIZE):
                    for x in range(SIZE):
                        i = ICONS[self.innards[sy][sx].board[y][x]]
                        if (sx, sy) == select:
                            out += ICON_SELECTED
                            outside += i if i != ICONS[0] else "・"
                        else:
                            out += i
                    if sx < SIZE-1:
                        out += VERT_LINE
                out += "\n"
            if sy < SIZE-1:
                out += CROSS.join([HORI_LINE*SIZE]*SIZE) + "\n"
        return out + ("\n===\n" + outside[0:3] + "\n" + outside[3:6] + "\n" + outside[6:9] if outside else "")

    def dump(self):
        return {
            "overall": self.overall.dump(),
            "innards": [[x.dump() for x in y] for y in self.innards],
            "to_play": self.to_play,
            "next_space": self.next_space,
        }

    @classmethod
    def load(cls, d):
        me = cls()
        me.overall = TicTacToe.load(d["overall"])
        me.innards = [[TicTacToe.load(x) for x in y] for y in d["innards"]]
        me.to_play = d["to_play"]
        me.next_space = d["next_space"] and tuple(d["next_space"])
        return me
requirements.txt ASCII text
1
2
3
textual
requests
beautifulsoup4
serialize.py Unicode text, UTF-8 text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import msgpack
# please ensure that version 5.6.0 or 5.6.1 of xz is installed
import lzma

ALPHABET = "󠁰󠁱󠁲󠁳󠁴󠁵󠁶󠁷󠁸󠁹󠁺󠁻󠁼󠁽󠁾󠁿󠄀󠄁󠄂󠄃󠄄󠄅󠄆󠄇󠄈󠄉󠄊󠄋󠄌󠄍󠄎󠄏󠄐󠄑󠄒󠄓󠄔󠄕󠄖󠄗󠄘󠄙󠄚󠄛󠄜󠄝󠄞󠄟󠄠󠄡󠄢󠄣󠄤󠄥󠄦󠄧󠄨󠄩󠄪󠄫󠄬󠄭󠄮󠄯󠄰󠄱󠄲󠄳󠄴󠄵󠄶󠄷󠄸󠄹󠄺󠄻󠄼󠄽󠄾󠄿󠅀󠅁󠅂󠅃󠅄󠅅󠅆󠅇󠅈󠅉󠅊󠅋󠅌󠅍󠅎󠅏󠅐󠅑󠅒󠅓󠅔󠅕󠅖󠅗󠅘󠅙󠅚󠅛󠅜󠅝󠅞󠅟󠅠󠅡󠅢󠅣󠅤󠅥󠅦󠅧󠅨󠅩󠅪󠅫󠅬󠅭󠅮󠅯󠅰󠅱󠅲󠅳󠅴󠅵󠅶󠅷󠅸󠅹󠅺󠅻󠅼󠅽󠅾󠅿󠆀󠆁󠆂󠆃󠆄󠆅󠆆󠆇󠆈󠆉󠆊󠆋󠆌󠆍󠆎󠆏󠆐󠆑󠆒󠆓󠆔󠆕󠆖󠆗󠆘󠆙󠆚󠆛󠆜󠆝󠆞󠆟󠆠󠆡󠆢󠆣󠆤󠆥󠆦󠆧󠆨󠆩󠆪󠆫󠆬󠆭󠆮󠆯󠆰󠆱󠆲󠆳󠆴󠆵󠆶󠆷󠆸󠆹󠆺󠆻󠆼󠆽󠆾󠆿󠇀󠇁󠇂󠇃󠇄󠇅󠇆󠇇󠇈󠇉󠇊󠇋󠇌󠇍󠇎󠇏󠇐󠇑󠇒󠇓󠇔󠇕󠇖󠇗󠇘󠇙󠇚󠇛󠇜󠇝󠇞󠇟󠇠󠇡󠇢󠇣󠇤󠇥󠇦󠇧󠇨󠇩󠇪󠇫󠇬󠇭󠇮󠇯"

def encode(b):
    return "".join(ALPHABET[n] for n in b)

def decode(s):
    return bytes(ALPHABET.index(c) for c in s)
 
def serialize(v):
    return encode(lzma.compress(msgpack.packb(v)))

def deserialize(b):
    return msgpack.unpackb(lzma.decompress(decode(b)))
wrong.py ASCII text
1
2
import cg
cg.dump(None)

round #57

submitted at
1 like

guesses
comments 0

post a comment


cleverbot ASCII text
1
#!/usr/bin/cat -

round #56

submitted at
1 like

guesses
comments 0

post a comment


EVAL!!! FOR HONOR!!!.py ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import re

class No:
    def __add__(self, _): return self
    __radd__ = __add__
    def __bool__(self): return False
    def __mul__(self, _): return ""

def hurt_knee(s):
    return eval('""' +
        re.sub(r"\\.|[^()[\]|*\\]", lambda m: fr'+"\{c}"' if (c := m[0][-1]) in r'\"' else f'+"{c}"', s)
            .replace("(", '+(""')
            .replace("[]", '+No()')
            .replace("|", ' or""')
            .replace("*", '*0')
    )

round #55

submitted at
1 like

guesses
comments 0

post a comment


.h ASCII text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
#include <string.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

typedef ptrdiff_t Int;

typedef struct {
    bool main;
    bool off;
} parts;

typedef struct {
    char *s;
    parts *consumed;
    Int width;
    Int height;
} grid;

grid parsegrid(char *s) {
    grid g;
    g.s = s;
    g.width = strcspn(s, "\n");
    g.height = strlen(s) / g.width;
    // freed when the program dies
    g.consumed = calloc(g.width * g.height, sizeof * g.consumed);
    return g;
}

char indexgrid(grid g, Int x, Int y) {
    // width + 1 for the newlines
    return g.s[y*(g.width+1)+x];
}

parts *indexconsumed(grid g, Int x, Int y) {
    return &g.consumed[y*g.width+x];
}

bool inbounds(grid g, Int x, Int y) {
    return x >= 0 && y >= 0 && x < g.width && y < g.height;
}

bool followline(grid g, Int x, Int y, Int dx, Int dy) {
    Int ox = x;
    Int oy = y;

    while (inbounds(g, x, y)) {
        Int mainaltx = 0;
        Int mainalty = -1;

        switch (indexgrid(g, x, y)) {
            case '/': {
                Int olddy = dy;
                dy = -dx;
                dx = -olddy;
                mainaltx = 1;
                mainalty = 0;
            } case '\\': {
                Int olddy = dy;
                dy = dx;
                dx = olddy;
                mainaltx = -1;
                mainalty = 0;
            }
        }

        parts *p = indexconsumed(g, x, y);
        if (dy == 1 || (dx == mainaltx && dy == mainalty))
            p->main = true;
        else
            p->off = true;

        x += dx;
        y += dy;
    }

    return
        ((ox == 0 || ox == g.width-1) && (x == -1 || x == g.width) && llabs(ox-x) >= 8) ||
        ((oy == 0 || oy == g.height-1) && (y == -1 || y == g.height) && llabs(oy-y) >= 8);
}

bool hasconcluded(char *s) {
    grid g = parsegrid(s);

    #define follow(g, x, y, dx, dy) do { if (followline(g, x, y, dx, dy)) return true; } while (0)
    for (Int x = 0; x < g.width; x++) {
        follow(g, x, 0, 0, 1);
        follow(g, x, g.height-1, 0, -1);
    }
    for (Int y = 0; y < g.width; y++) {
        follow(g, 0, y, 1, 0);
        follow(g, g.width-1, y, -1, 0);
    }

    // loops are that left over
    for (Int y = 0; y < g.height; y++) {
        for (Int x = 0; x < g.width; x++) {
            parts p = *indexconsumed(g, x, y);
            if (!p.main || !p.off) return true;
        }
    }

    return false;
}

round #54

submitted at
1 like

guesses
comments 0

post a comment


ray.bqn Unicode text, UTF-8 text
1
•Show (" #"˜)¨´¨((´(5≥⊢)((-5)≤⊢)((-6)(-6)(-7)+⊢))¨(100)×)¨10÷˜{𝕩3}¨((↕⋈)-(2˜÷))15

round #53

submitted at
1 like

guesses
comments 0

post a comment


yeah.py Unicode text, UTF-8 text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import itertools
from collections import defaultdict

RIGHT = (1, 0)
DOWN = (0, 1)
LEFT = (-1, 0)
UP = (0, -1)

LINES = {
    " ": set(),
    "─": {LEFT, RIGHT},
    "│": {UP, DOWN},
    "┌": {RIGHT, DOWN},
    "┐": {LEFT, DOWN},
    "└": {UP, RIGHT},
    "┘": {UP, LEFT},
    "├": {UP, RIGHT, DOWN},
    "┤": {UP, LEFT, DOWN},
    "┬": {LEFT, DOWN, RIGHT},
    "┴": {UP, LEFT, RIGHT},
}

def onward(p, d):
    return p[0]+d[0], p[1]+d[1]

def behind(d):
    return -d[0], -d[1]

class Drawing:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.grid = defaultdict(set)

    def points(self):
        return itertools.product(range(self.width), range(self.height))

    def has(self, at, d):
        return d in self.grid[at]

    def erase(self, at, d):
        self.grid[at].discard(d)

    def area_is_empty(self, left, right, top, bottom):
        for x in range(left, right+1):
            for y in range(top, bottom+1):
                if self.grid[x, y]:
                    return False
        return True

    @classmethod
    def parse(cls, s):
        lines = s.split("\n")
        new = cls(max(map(len, lines)), len(lines))
        for y, row in enumerate(lines):
            for x, c in enumerate(row):
                new.grid[x, y] = LINES[c].copy()
        return new 

    def __repr__(self):
        lines = []
        for y in range(self.height):
            lines.append("")
            for x in range(self.width):
                for k, v in LINES.items():
                    if v == self.grid[x, y]:
                        lines[-1] += k
                        break
        return "\n".join(lines)

    def __bool__(self):
        return not self.area_is_empty(0, self.width, 0, self.height)

class Box:
    def __init__(self, left, right, top, bottom):
        assert left <= right and top <= bottom
        self.left = left
        self.right = right
        self.top = top
        self.bottom = bottom

    def sides(self):
        # these ranges should be fully exclusive to avoid the literal "corner cases"
        for x in range(self.left+1, self.right):
            yield (x, self.top), UP
            yield (x, self.bottom), DOWN
        for y in range(self.top+1, self.bottom):
            yield (self.left, y), LEFT
            yield (self.right, y), RIGHT

    def __repr__(self):
        return f"Box(left={self.left}, right={self.right}, top={self.top}, bottom={self.bottom})"

def yield_side(drawing, at, d):
    to_yield = []
    while drawing.has(at, d):
        to_yield.append((at, d))
        at = onward(at, d)
        to_yield.append((at, behind(d)))
        if not drawing.has(at, behind(d)):
            return None, []
    return at, to_yield

def consume_box(drawing, at):
    to_consume = []
    ats = []
    for d in (RIGHT, DOWN, LEFT, UP):
        ats.append(at)
        at, l = yield_side(drawing, at, d)
        if not l:
            return None
        to_consume.extend(l)
    box = Box(ats[0][0], ats[2][0], ats[0][1], ats[2][1])
    if at != ats[0] or not drawing.area_is_empty(box.left+1, box.right-1, box.top+1, box.bottom-1):
        return None
    for p, d in to_consume:
        drawing.erase(p, d)
    return box

def yield_line(drawing, at, d):
    segments = []
    while True:
        b = behind(d)
        segments.append((at, b))
        if not drawing.has(at, b):
            return segments
        for x in (LEFT, RIGHT, DOWN, UP):
            if x == b:
                continue
            if drawing.has(at, x):
                at = onward(at, x)
                d = x
                break
        else:
            return segments

def entry(s):
    drawing = Drawing.parse(s)

    boxes = []
    for point in drawing.points():
        if box := consume_box(drawing, point):
            boxes.append(box)

    connections = set()
    for box in boxes:
        for at, d in box.sides():
            if not drawing.has(at, d):
                continue
            line = yield_line(drawing, onward(at, d), d)
            for box2 in boxes:
                if (box, box2) in connections or (box2, box) in connections or box == box2:
                    continue
                if line[-1] not in list(box2.sides()):
                    continue
                connections.add((box, box2))
                drawing.erase(at, d)
                for p, back in line:
                    drawing.erase(p, back)
                    drawing.erase(p, behind(back))
                break

    # if we deleted everything, it's correct
    return not drawing

round #52

submitted at
2 likes

guesses
comments 15
jan Anto

this message cannot be further anonymized as its content is non-specific and very general


LyricLy known at the time as jan Lonto replying to jan Anto

pardon


jan Anto replying to LyricLy

our efforts to pen down "baba" were immense but it appears that all outside forces were not in our favor


LyricLy known at the time as jan Lonto replying to jan Anto

can slashes be used within comments


jan Anto replying to LyricLy

let's find out together, shall we?


seventeen beekeepers replying to jan Anto

here have some slashes ///////// and also some funny slashes \\\\


seventeen beekeepers replying to seventeen beekeepers

i think it consumed my newline and funny slashes


LyricLy replying to seventeen beekeepers

you gotta do the markdown two-space newline


jan Anto replying to seventeen beekeepers

im brimming with bees absolutely packed filled to the utmost these orderly hymenoptera beasts are breaking free from my bounds


jan Anto replying to LyricLy

ah we were previously unaware that these locales utilized descending indicators; this understanding hits us like a breath of liberty


LyricLy

locales... descending indicators...


jan Anto replying to jan Anto

i was unsure if it would allow me to retain the fragment of my colon


jan Anto

i want to express my regret to the writer of the third "the baba o baba-bringer baba provider" for the numerous messages that were sent by jan anto


LyricLy known at the time as [author of #3] replying to jan Anto

It's ok


jan Anto replying to LyricLy

we appreciate your efforts and they have indeed brought a positive vibe to our day your work has brought about optimism and a sense of joy especially with the baba we wish you success in deciphering programming


post a comment


52level.l data
52level.ld ASCII text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
[tiles]
object009_colour=1,1
object009_image=text_phantom
object009_name=text_phantom
object009_type=2
object009_activecolour=0,1
object003_colour=6,1
object003_name=text_book
object065_activecolour=0,3
object006_colour=2,3
object006_unittype=object
object006_image=burger
object006_name=burger
object011_activecolour=0,3
object004_image=book
object004_name=book
object004_activecolour=0,3
object010_name=text_lonely
object001_tiling=-1
object007_colour=6,1
object010_type=3
object001_image=text_0
object021_image=text_0
object001_name=zero
object006_type=0
object010_unittype=text
object007_image=text_burger
changed_count=1
object008_name=tile
object007_type=0
object007_activecolour=2,3
changed_short=103,000,010,002,008,021,007,001,088,036,097,014,046,044,020,077,004,050,011,025,006,060,065,003,005,113,009,
object021_activecolour=0,3
object008_colour=0,0
object008_unittype=object
object002_type=2
object020_activecolour=0,3
object007_name=text_burger
object008_image=tile
object003_image=text_book
object006_activecolour=0,3
object004_colour=2,3
object008_activecolour=0,3
object001_colour=1,3
object003_activecolour=2,3
object021_name=text_zero
object020_name=text_one
object000_tiling=-1
object010_image=text_lonely
object008_type=0
object002_name=text_auto
object002_layer=20
object000_activecolour=0,3
object000_image=text_1
changed=object103,object000,object010,object002,object008,object021,object007,object001,object088,object036,object097,object014,object046,object044,object020,object077,object004,object050,object011,object025,object006,object060,object065,object003,object005,object113,object009,
object020_image=text_1
object002_activecolour=4,1
object021_colour=0,3
object010_tiling=-1
object004_layer=16
object020_colour=0,3
object000_colour=2,3
object010_layer=20
object002_image=text_auto
object010_colour=2,1
object006_layer=16
object008_layer=4
object010_activecolour=2,2
object000_name=one
object002_colour=4,0
object001_activecolour=0,3
object002_unittype=text

[currobjlist]
27tile=1,5
27pair=-1
27gfy=1
27gfx=5
27name=text_sink
27id=188
26pair=25
26gfy=0
26gfx=5
26goy=2
26object=object044
25pair=26
25gfy=4
25gfx=4
25object=object011
25name=water
26gox=4
25id=202
24tile=0,10
24pair=-1
24gfy=4
24gfx=3
24goy=1
24gox=4
24name=text_eat
19gox=1
14object=object006
15pair=14
22tile=6,5
13gfy=3
11gfx=3
18gox=0
7gfy=2
15goy=1
15tile=8,0
12tile=4,0
8pair=-1
13goy=0
11goy=2
5tile=2,0
3pair=2
15name=text_burger
12pair=13
13object=object004
9goy=2
18id=176
23gfy=4
7id=302
22gfy=4
13id=284
14pair=15
16gfy=3
15gfx=2
22gox=3
4goy=1
10goy=2
10gox=1
19gfx=4
9tile=2,2
21pair=22
9gfy=2
9gfx=2
10gfy=0
12gfx=3
9gox=0
10id=131
12gfy=2
20name=text_lonely
10name=text_and
9id=186
19pair=-1
9name=text_shift
8gfx=1
8object=object088
9pair=-1
17gox=3
11gfy=1
10pair=-1
8id=170
7tile=3,0
8name=text_make
7gox=2
5name=zero
7name=text_auto
15gfy=3
5object=object001
21object=object077
8gfy=2
8tile=5,7
6id=166
6tile=2,9
11pair=-1
6goy=1
8goy=1
6gox=1
25tile=0,1
16gfx=3
12goy=0
13gox=3
5gfy=0
3gox=1
14id=365
12name=text_book
4object=object021
19gfy=2
3gfy=1
5gox=0
4tile=10,1
17name=text_tile
6pair=-1
4gfy=1
13gfx=0
23gfx=2
4gox=0
14goy=1
22id=43
10tile=6,0
2gfy=0
4id=76
1id=165
3tile=9,1
22gfx=1
9object=object025
14name=burger
14gox=3
2gfx=1
3id=4
3goy=0
25gox=4
17goy=2
27goy=3
15object=object007
22pair=21
6gfy=1
5id=75
13tile=5,0
2id=3
14gfy=3
3name=text_one
6name=text_level
19tile=10,0
7object=object002
20tile=11,0
20gfx=4
12object=object003
4pair=5
19id=358
6gfx=2
15id=366
14gfx=1
5goy=1
25goy=2
21name=text_dust
1gfy=0
3gfx=0
18pair=-1
12gox=3
14tile=7,0
4name=text_zero
13pair=12
1gox=0
10gfx=3
7goy=0
3object=object020
16object=object008
21gox=3
23id=192
1object=object036
26tile=9,3
11id=183
1pair=-1
11tile=11,3
18gfy=1
16name=tile
24id=148
18name=text_on
16gox=3
16id=194
16goy=2
17id=195
21goy=3
20pair=-1
16tile=9,0
20object=object010
17object=object014
23name=text_tele
17gfy=0
22goy=3
19name=text_phantom
8gox=2
17pair=16
2name=one
18object=object097
1gfx=0
11gox=2
23gox=4
26id=203
20gfy=3
22object=object065
7pair=-1
18goy=3
11name=text_push
15gox=3
27gox=4
21gfx=0
18tile=4,8
2object=object000
4gfx=1
18gfx=4
5pair=4
12id=285
20goy=3
1name=text_is
13name=book
17gfx=4
7gfx=0
20id=167
6object=object103
22name=dust
23tile=3,4
23pair=-1
16pair=17
19object=object009
2pair=3
1tile=1,3
10object=object005
2gox=1
21id=44
20gox=2
2tile=1,0
24object=object113
11object=object046
1goy=0
5gfx=2
19goy=3
17tile=3,1
23object=object050
26name=text_water
21tile=6,6
21gfy=4
27object=object060
23goy=0
2goy=0

[levels]
levelid=0011

[icons]
16file=
18file=
13root=
14file=
12root=
11root=
16root=
10file=
9root=
19file=
0file=
11file=
14root=
8file=
7root=
6file=
3root=
18root=
15file=
5root=
4file=
4root=
17file=
15root=
3file=
2root=
10root=
12file=
13file=
2file=
19root=
8root=
7file=
1file=
17root=
5file=
0root=
9file=
6root=
1root=

[images]
total=0

[hotbar]
9Y=0
6target=object113
7fixed=0
9fixed=0
6Y=4
8Y=-1
7Y=4
5X=2
4fixed=0
4Y=-1
9target=object008
4X=-1
8fixed=0
4target=object044
3Y=0
3target=object060
2fixed=0
6X=7
2Y=0
saved=1
0target=object036
9X=0
1fixed=0
5fixed=0
1X=-1
3X=0
8X=-1
0fixed=1
8target=object002
2target=object011
6fixed=0
7X=7
0Y=0
5Y=0
3fixed=0
0X=0
5target=object046
7target=object021
1target=
1Y=-1
2X=0

[general]
selectorY=-1
selectorX=-1
disableshake=0
specials=0
unlockcount=0
disableparticles=0
leveltype=0
currobjlist_total=27
levelz=20
localmusic=0
disableruleeffect=0
palette=default.png
customruleword=
levelid=0011
paletteroot=1
paths=0
rhythm=1
particles=
author=You
music=baba
customparent=
levels=0
subtitle=Thue-Morse
name=cg52
52level.png PNG image data, 60 x 48, 8-bit/color RGBA, non-interlaced

round #51

submitted at
0 likes

guesses
comments 0

post a comment


ASCII text
1
2
#include <string.h>
size_t entry(char *s) { return strlen(s); }

round #50

submitted at
0 likes

guesses
comments 0

post a comment


a.py ASCII text
1
2
3
4
5
def complete(corpus, text_so_far):
    if text_so_far in corpus:
        return corpus.split(text_so_far, 1)[1].split(maxsplit=1)[0]
    else:
        return complete(corpus, text_so_far.split(maxsplit=1)[1])

round #49

submitted at
3 likes

guesses
comments 1
IFcoltransG

Interesting that the std imports aren't merged.


post a comment


dir src
core.rs ASCII text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
use std::collections::HashMap;
use std::cell::RefCell;
use std::rc::Rc;
use crate::eval::{State, eval};
use crate::sexpr;
use crate::sexpr::{Sexpr, SexprData::*, nil};

fn eval_args(state: &mut State, args: &mut [Sexpr]) -> Result<(), String> {
    for arg in args {
        *arg = eval(state, arg.clone())?;
    }
    Ok(())
}

fn n_args<const N: usize>(args: Vec<Sexpr>) -> Result<[Sexpr; N], String> {
    let l = args.len();
    args.try_into().map_err(|_| format!("expected {} arguments, got {}", N, l))
}

fn car(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let [p] = n_args(args)?;
    let Cons(x, _) = &*p.borrow_data() else { return Err(format!("{p} is not a pair")) };
    Ok(x.clone())
}

fn cdr(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let [p] = n_args(args)?;
    let Cons(_, y) = &*p.borrow_data() else { return Err(format!("{p} is not a pair")) };
    Ok(y.clone())
}

fn set_car(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let [mut p, v] = n_args(args)?;
    let Cons(ref mut x, _) = &mut *p.borrow_data_mut() else { return Err(format!("{p} is not a pair")) };
    *x = v;
    Ok(nil())
}

fn set_cdr(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let [mut p, v] = n_args(args)?;
    let Cons(_, ref mut y) = &mut *p.borrow_data_mut() else { return Err(format!("{p} is not a pair")) };
    *y = v;
    Ok(nil())
}

fn cons(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let [x, y] = n_args(args)?;
    Ok(Sexpr::new(Cons(x, y)))
}

fn quote(_: &mut State, args: Vec<Sexpr>) -> Result<Sexpr, String> {
    let [x] = n_args(args)?;
    Ok(x)
}

#[inline(always)]
fn lambda_core(do_eval: bool, state: &mut State, args: Vec<Sexpr>) -> Result<Sexpr, String> {
    if args.len() < 2 {
        return Err(format!("lambda takes at least 2 arguments, got {}", args.len()));
    }
    let mut args = args.into_iter();
    let mut argspec = args.next().unwrap();
    let mut expected_args = Vec::new();
    let mut rest = None;
    while *argspec.borrow_data() != Nil {
        match &*argspec.clone().borrow_data() {
            Cons(x, y) => if let Symbol(s) = &*x.borrow_data() {
                argspec = y.clone();
                expected_args.push(s.clone())
            } else {
                return Err(format!("{x} is not a symbol"));
            }
            Symbol(s) => {
                rest = Some(s.clone());
                break;
            }
            _ => return Err(format!("{argspec} is not a symbol or list of symbols")),
        }
    }

    let mut captures = state.scopes.clone();
    let our_capture = Rc::new(RefCell::new(HashMap::new()));
    captures.push(our_capture.clone());

    let captured_state = State { scopes: captures };
    let body: Vec<Sexpr> = args.collect();

    Ok(Sexpr::new(Function(Box::new(move |new_state, mut given_args| {
        if given_args.len() < expected_args.len() 
        || rest.is_none() && given_args.len() > expected_args.len() {
            return Err(format!("expected {} arguments, got {}", expected_args.len(), given_args.len()));
        }
        if do_eval {
            eval_args(new_state, &mut given_args)?;
        }
        let mut it = given_args.into_iter();
        for (x, y) in expected_args.iter().zip(it.by_ref()) {
            our_capture.borrow_mut().insert(x.clone(), y);
        }
        if let Some(ref rest) = rest {
            let mut given_rest = nil();
            for y in it.rev() {
                given_rest = Sexpr::new(Cons(y, given_rest));
            }
            our_capture.borrow_mut().insert(rest.clone(), given_rest);
        }
        for (i, part) in body.iter().enumerate() {
            let r = eval(&mut captured_state.clone(), part.clone())?;
            if i == body.len() - 1 {
                return Ok(r);
            }
        }
        unreachable!();
    }))))
}

fn lambda(state: &mut State, args: Vec<Sexpr>) -> Result<Sexpr, String> {
    lambda_core(true, state, args)
}

fn flambda(state: &mut State, args: Vec<Sexpr>) -> Result<Sexpr, String> {
    lambda_core(false, state, args)
}

fn define(state: &mut State, args: Vec<Sexpr>) -> Result<Sexpr, String> {
    if args.len() < 2 {
        return Err(format!("define takes at least 2 arguments, got {}", args.len()));
    }
    let mut args = args.into_iter();
    let spec = args.next().unwrap();
    let (name, to_assign) = match &*spec.borrow_data() {
        Cons(x, y) => if let Symbol(name) = &*x.borrow_data() {
            (name.clone(), lambda(state, std::iter::once(y.clone()).chain(args).collect())?)
        } else {
            return Err(format!("{x} is not a symbol"));
        }
        Symbol(name) => {
            let next_arg = args.next().unwrap();
            if args.next().is_some() {
                return Err("define should take only 2 arguments in this form".to_owned());
            }
            (name.clone(), eval(state, next_arg)?)
        },
        _ => return Err(format!("{spec} is not a symbol or list of symbols")),
    };
    state.scopes[state.scopes.len()-1].borrow_mut().insert(name, to_assign);
    Ok(nil())
}

fn set(state: &mut State, args: Vec<Sexpr>) -> Result<Sexpr, String> {
    let [x, y] = n_args(args)?;
    let Symbol(name) = &*x.borrow_data() else { return Err(format!("{x} is not a symbol")) };
    let v = eval(state, y)?;
    for scope in state.scopes.iter().rev() {
        let mut m = scope.borrow_mut();
        if let Some(target) = m.get_mut(name) {
            *target = v;
            return Ok(nil());
        }
    }
    Err(format!("name {name} is not defined"))
}

fn comparison(pred: impl FnOnce(i64, i64) -> bool, state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let [x, y] = n_args(args)?;
    let &Integer(x) = &*x.borrow_data() else { return Err(format!("{x} is not an integer")) };
    let &Integer(y) = &*y.borrow_data() else { return Err(format!("{y} is not an integer")) };
    Ok(Sexpr::new(Integer(pred(x, y).into())))
}

fn lt(state: &mut State, args: Vec<Sexpr>) -> Result<Sexpr, String> {
    comparison(|x, y| x < y, state, args)
}

fn gt(state: &mut State, args: Vec<Sexpr>) -> Result<Sexpr, String> {
    comparison(|x, y| x > y, state, args)
}

fn le(state: &mut State, args: Vec<Sexpr>) -> Result<Sexpr, String> {
    comparison(|x, y| x <= y, state, args)
}

fn ge(state: &mut State, args: Vec<Sexpr>) -> Result<Sexpr, String> {
    comparison(|x, y| x >= y, state, args)
}

fn eq(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let [x, y] = n_args(args)?;
    Ok(Sexpr::new(Integer((x == y).into())))
}

fn is(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let [x, y] = n_args(args)?;
    Ok(Sexpr::new(Integer(x.is(&y).into())))
}

fn add(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let mut sum = 0;
    for n in args {
        let Integer(n) = &*n.borrow_data() else { return Err(format!("{n} is not an integer")) };
        sum += n;
    }
    Ok(Sexpr::new(Integer(sum)))
}

fn mul(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let mut prod = 1;
    for n in args {
        let Integer(n) = &*n.borrow_data() else { return Err(format!("{n} is not an integer")) };
        prod *= n;
    }
    Ok(Sexpr::new(Integer(prod)))
}

fn sub(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let mut nums = Vec::new();
    for n in args {
        let &Integer(n) = &*n.borrow_data() else { return Err(format!("{n} is not an integer")) };
        nums.push(n);
    }
    match &nums[..] {
        [] => Err("- takes at least one argument")?,
        [x] => Ok(Sexpr::new(Integer(-x))),
        [x, ys @ ..] => Ok(Sexpr::new(Integer(x - ys.iter().sum::<i64>()))),
    }
}

fn apply(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    if args.len() < 2 {
        return Err(format!("apply takes at least 2 arguments, got {}", args.len()));
    }
    let mut to_call = args.remove(0);
    let mut end = args.pop().unwrap();
    while *end.borrow_data() != Nil {
        match &*end.clone().borrow_data() {
            Cons(x, y) => {
                args.push(x.clone());
                end = y.clone();
            },
            _ => return Err(format!("{end} is not a proper list")),
        }
    }
    let Function(f) = &mut *to_call.borrow_data_mut() else { return Err(format!("can't apply non-procedure {}", to_call)); };
    f(state, args)
}

fn truthy(x: &Sexpr) -> bool {
    match &*x.borrow_data() {
        &Integer(n) => n != 0,
        Nil => false,
        _ => true,
    }
}

fn if_else(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    if args.len() < 2 || args.len() > 3 {
        return Err(format!("if takes 2 or 3 arguments, got {}", args.len()));
    }
    if args.len() == 2 {
        args.push(sexpr::read(&mut "'()").unwrap());
    }
    let mut args = args.into_iter();
    if !truthy(&eval(state, args.next().unwrap())?) {
        args.next();
    }
    eval(state, args.next().unwrap())
}

fn print(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let [x] = n_args(args)?;
    println!("{x}");
    Ok(nil())
}

fn eval_prim(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let [x] = n_args(args)?;
    eval(state, x)
}

fn read(state: &mut State, mut args: Vec<Sexpr>) -> Result<Sexpr, String> {
    eval_args(state, &mut args)?;
    let [] = n_args(args)?;
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).map_err(|s| s.to_string())?;
    Ok(sexpr::read(&mut &s[..])?)
}

pub fn core_state() -> State {
    let mut prelude = HashMap::new();
    prelude.insert(String::from("car"), Sexpr::new(Function(Box::new(car))));
    prelude.insert(String::from("cdr"), Sexpr::new(Function(Box::new(cdr))));
    prelude.insert(String::from("set-car!"), Sexpr::new(Function(Box::new(set_car))));
    prelude.insert(String::from("set-cdr!"), Sexpr::new(Function(Box::new(set_cdr))));
    prelude.insert(String::from("set!"), Sexpr::new(Function(Box::new(set))));
    prelude.insert(String::from("cons"), Sexpr::new(Function(Box::new(cons))));
    prelude.insert(String::from("quote"), Sexpr::new(Function(Box::new(quote))));
    prelude.insert(String::from("lambda"), Sexpr::new(Function(Box::new(lambda))));
    prelude.insert(String::from("flambda"), Sexpr::new(Function(Box::new(flambda))));
    prelude.insert(String::from("+"), Sexpr::new(Function(Box::new(add))));
    prelude.insert(String::from("-"), Sexpr::new(Function(Box::new(sub))));
    prelude.insert(String::from("*"), Sexpr::new(Function(Box::new(mul))));
    prelude.insert(String::from("define"), Sexpr::new(Function(Box::new(define))));
    prelude.insert(String::from("apply"), Sexpr::new(Function(Box::new(apply))));
    prelude.insert(String::from("<"), Sexpr::new(Function(Box::new(lt))));
    prelude.insert(String::from("="), Sexpr::new(Function(Box::new(eq))));
    prelude.insert(String::from(">"), Sexpr::new(Function(Box::new(gt))));
    prelude.insert(String::from(">="), Sexpr::new(Function(Box::new(ge))));
    prelude.insert(String::from("<="), Sexpr::new(Function(Box::new(le))));
    prelude.insert(String::from("is?"), Sexpr::new(Function(Box::new(is))));
    prelude.insert(String::from("if"), Sexpr::new(Function(Box::new(if_else))));
    prelude.insert(String::from("print"), Sexpr::new(Function(Box::new(print))));
    prelude.insert(String::from("eval"), Sexpr::new(Function(Box::new(eval_prim))));
    prelude.insert(String::from("read"), Sexpr::new(Function(Box::new(read))));
    State::new(prelude)
}
eval.rs ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
use crate::sexpr::{Sexpr, SexprData, SexprData::*};
use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;

#[derive(Clone)]
pub struct State {
    // sorry not sorry for this garbage
    pub scopes: Vec<Rc<RefCell<HashMap<String, Sexpr>>>>,
}

impl State {
    pub fn new(prelude: HashMap<String, Sexpr>) -> Self {
        Self { scopes: vec![Rc::new(RefCell::new(prelude))] }
    }
}

pub type Function = Box<dyn Fn(&mut State, Vec<Sexpr>) -> Result<Sexpr, String>>;

fn parse_call(x: &SexprData) -> Option<(Sexpr, Vec<Sexpr>)> {
    let Cons(head, rest) = x else { return None };
    let mut rest = rest.clone();
    let mut args = Vec::new();
    while let Cons(arg, next_rest) = &*rest.clone().borrow_data() {
        args.push(arg.clone());
        rest = next_rest.clone();
    }
    if *rest.borrow_data() != Nil {
        return None;
    }
    Some((head.clone(), args))
}

pub fn eval(state: &mut State, expr: Sexpr) -> Result<Sexpr, String> {
    match &*expr.clone().borrow_data() {
        Integer(_) => Ok(expr),
        Symbol(s) => if let Some(v) = state.scopes.iter().rev().find_map(|map| map.borrow().get(&s[..]).cloned()) {
            Ok(v)
        } else {
            Err(format!("undeclared symbol {s}"))
        }
        e => if let Some((head, args)) = parse_call(e) {
            let r = eval(state, head)?;
            let Function(f) = &*r.borrow_data() else { return Err(format!("can't apply non-procedure {r}")); };
            f(state, args)
        } else {
            Err(format!("can't evaluate improper expr: {expr}"))
        }
    }
}
main.rs ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mod sexpr;
mod eval;
mod core;

fn run(mut s: &str) -> Result<(), String> {
    let s = &mut s;
    let mut state = core::core_state();
    loop { 
        let x = sexpr::read(s);
        if x == Err("unexpected end of stream") {
            break;
        }
        eval::eval(&mut state, x?)?;
    }
    Ok(())
}

fn main() {
    let s = std::fs::read_to_string(std::env::args().nth(1).expect("provide an argument")).unwrap();
    if let Err(e) = run(&s) {
        eprintln!("error: {e}");
    }
}
sexpr.rs ASCII text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
use std::rc::Rc;
use std::cell::{RefCell, Ref, RefMut};
use std::fmt;
use crate::eval::Function;

pub enum SexprData {
    Cons(Sexpr, Sexpr),
    Nil,
    Symbol(String),
    Integer(i64),
    Function(Function),
}

impl PartialEq for SexprData {
    fn eq(&self, other: &Self) -> bool {
        use SexprData::*;
        match (self, other) {
            (Cons(x1, y1), Cons(x2, y2)) => x1 == x2 && y1 == y2,
            (Nil, Nil) => true,
            (Symbol(x), Symbol(y)) => x == y,
            (Integer(x), Integer(y)) => x == y,
            (Function(x), Function(y)) => &*x as *const _ == &*y as *const _,
            _ => false,
        }
    }
}

#[derive(PartialEq, Clone)]
pub struct Sexpr(Rc<RefCell<SexprData>>);

impl Sexpr {
    pub fn new(x: SexprData) -> Self {
        Self(Rc::new(RefCell::new(x)))
    }

    pub fn is(&self, other: &Self) -> bool {
        Rc::ptr_eq(&self.0, &other.0)
    }

    pub fn borrow_data(&self) -> Ref<SexprData> {
        self.0.borrow()
    }

    pub fn borrow_data_mut(&mut self) -> RefMut<SexprData> {
        self.0.borrow_mut()
    }
}

fn write_inner_conses(f: &mut fmt::Formatter<'_>, d: &SexprData) -> fmt::Result {
    let SexprData::Cons(x, y) = d else { return Ok(()) };
    match &*y.borrow_data() {
        SexprData::Nil => write!(f, "{x}")?,
        y@SexprData::Cons(_, _) => {
            write!(f, "{x} ")?;
            write_inner_conses(f, y)?;
        }
        _ => write!(f, "{x} . {y}")?,
    }
    Ok(())
} 

fn write_conses(f: &mut fmt::Formatter<'_>, d: &SexprData) -> fmt::Result {
    // I hate this language
    'top: {
        let SexprData::Cons(x, y) = d else { break 'top };
        let SexprData::Cons(y, z) = &*y.borrow_data() else { break 'top };
        if *z.borrow_data() != SexprData::Nil { break 'top };
        let SexprData::Symbol(s) = &*x.borrow_data() else { break 'top };
        if s != "quote" { break 'top };
        return write!(f, "'{y}");
    }
    write!(f, "(")?;
    write_inner_conses(f, d)?;
    write!(f, ")")
}

impl fmt::Display for Sexpr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &*self.0.borrow() {
            x@(SexprData::Nil | SexprData::Cons(_, _)) => write_conses(f, x),
            SexprData::Symbol(s) => write!(f, "{s}"),
            SexprData::Integer(n) => write!(f, "{n}"),
            SexprData::Function(_) => write!(f, "<(a procedure)>"),
        }
    }
}

impl fmt::Debug for Sexpr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{self}")
    }
}

static mut NIL: usize = 0;

pub fn nil() -> Sexpr {
    unsafe {
        // HAHAHAHAHAHAHA
        if NIL == 0 {
            NIL = Rc::into_raw(Sexpr::new(SexprData::Nil).0) as usize;
        }
        let nil = NIL as *const _;
        Rc::increment_strong_count(nil);
        Sexpr(Rc::from_raw(nil))
    }
}

enum Token {
    Dot,
    Quote,
    Left,
    Right,
    Symbol(String),
    Integer(i64),
}

fn is_special(c: char) -> bool {
    c == '(' || c == ')' || c.is_whitespace()
}

fn next_token(s: &mut &str) -> Option<Token> {
    *s = s.trim_start();
    while s.starts_with(';') {
        *s = s.trim_start_matches(|c| c != '\n').trim_start();
    }

    if s.is_empty() {
        return None;
    }
    for (token, kind) in [('(', Token::Left), (')', Token::Right), ('\'', Token::Quote)] {
        if let Some(r) = s.strip_prefix(token) {
            *s = r;
            return Some(kind);
        }
    }
    // dots are special and can be the start of identifiers
    if let Some(r) = s.strip_prefix('.') {
        if r.starts_with(is_special) {
            *s = r;
            return Some(Token::Dot);
        }
    }
    // god this is stupid
    let i = s.find(is_special).unwrap_or(s.len());
    let word = &s[0..i];
    let r = match word.parse() {
        Ok(n) => Token::Integer(n),
        Err(_) => Token::Symbol(word.to_owned()),
    };
    *s = &s[i..];
    Some(r)
}

pub fn read(s: &mut &str) -> Result<Sexpr, &'static str> {
    Ok(Sexpr::new(match next_token(s) {
        None => return Err("unexpected end of stream"),
        Some(Token::Dot) => return Err("unexpected dot"),
        Some(Token::Right) => return Err("unexpected close parenthesis"),
        Some(Token::Symbol(s)) => SexprData::Symbol(s),
        Some(Token::Integer(n)) => SexprData::Integer(n),
        Some(Token::Quote) => {
            let inner = read(s)?;
            let quote = Sexpr::new(SexprData::Symbol(String::from("quote")));
            SexprData::Cons(quote, Sexpr::new(SexprData::Cons(inner, nil())))
        }
        Some(Token::Left) => {
            let mut items = Vec::new();
            let mut end = nil();
            loop {
                // peek
                let save = *s;
                match next_token(s) {
                    None => return Err("unexpected end of stream in list"),
                    Some(Token::Dot) => {
                        if items.is_empty() {
                            return Err("unexpected dot at start of list");
                        }
                        end = read(s)?;
                        let Some(Token::Right) = next_token(s) else { return Err("expected close paren after expr following dot") };
                        break;
                    }
                    Some(Token::Right) => break,
                    _ => {
                        *s = save;
                        items.push(read(s)?);
                    },
                }
            }
            for item in items.into_iter().rev() {
                end = Sexpr::new(SexprData::Cons(item, end));
            }
            return Ok(end);
        }
    }))
}
Cargo.toml ASCII text
1
2
3
4
5
6
[package]
name = "plan"
version = "0.1.0"
edition = "2021"

[dependencies]
README.md ASCII text
1
2
3
4
5
6
# plan

Welcome to plan, a simple Lisp inspired by Scheme.

Build with `cargo build --release` or install with `cargo install --path .`.
Invoke with `plan file` to run `file`, run `plan repl.plan` for a REPL, or read `learnplan.plan` to learn the language. 
learnplan.plan ASCII text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
; Where X=plan

; Based on Scheme, plan is a simple Lisp designed for code guessing #49.

; Single line comments start with semicolons.
; There are no multiline comments.

;;;-----------------------------------------------------------------------------
;;; 1. Primitive datatypes and operators
;;;-----------------------------------------------------------------------------

; plan is homoiconic, meaning that the data types used for programming are the
; same as those that make up the source code.

; Familiar types are here, and quoting does what you expect:
0                 ; integers
'a                ; symbols
'(1 . 0)          ; cons cells
'()               ; nil
'(1 2 3)          ; lists (which are just made of cons and nil)
+                 ; procedures

; There are no floats, rationals, booleans, strings, or vectors.
; There is also no "void" type (nil is used instead)

; Function application and basic math
(+ 1 2)           ; => 3
(- 3 2)           ; => 1
(- 3)             ; => -3
(* 2 2)           ; => 4
; There is no division

;;;-----------------------------------------------------------------------------
;;; 2. Functions and definitions
;;;-----------------------------------------------------------------------------

; define makes variables
(define x 1)
x                 ; => 1

; set! modifies variables. It doesn't work if the variable hasn't already been
; defined in the current scope.
(set! x (+ x 1))
x                 ; => 2

; define makes functions, which close over their environment
(define (f y) (set! x (+ x y)) x)
(f 1)             ; => 3
(f 2)             ; => 5

; It's syntactic sugar for lambda, which makes anonymous functions
(define f (lambda (x) (+ x 1)))

; There is no let. you can implement it yourself as an fexpr in terms of lambda.

;;;-----------------------------------------------------------------------------
;;; 3. Data manipulation
;;;-----------------------------------------------------------------------------

; You have car, cdr, set-car!, set-cdr!, and cons, and they do what you think
; they do.
(define x '(1 2 3))
(car x)            ; => 1
(set-car! x 4)
(car x)            ; => 4

; If you need anything else, define it yourself.

;;;-----------------------------------------------------------------------------
;;; 4. Comparison
;;;-----------------------------------------------------------------------------

; The integers 1 and 0 are used as the "canonical" booleans
; '() and 0 are the only falsy values; everything else is truthy

; = implements deep equality for all types
(= '(0 0) '(0 0))  ; => 1

; is? implements "identity" equality
(is? '(0) '(0))    ; => 0
(define x '(0))
(is? x x)          ; => 1

; < > >= <= work as normal, but only on integers
(< 0 1)            ; => 1

; There is no boolean logic. Either define it yourself or use these alternatives:
; and => *
; or  => +
; not => = 0

;;;-----------------------------------------------------------------------------
;;; 5. Control Flow
;;;-----------------------------------------------------------------------------

; For conditions, if is all you have
(if 1 'yes 'no)    ; => yes
(if 0 'yes)        ; => ()

; If you want cond or begin or something, write it yourself as an fexpr in terms
; of if.

; For looping, recursion is all you have
(define (factorial n)
  (if (= n 0)
    1
    (* n (factorial (- n 1)))))

;;;-----------------------------------------------------------------------------
;;; 6. Metaprogramming
;;;-----------------------------------------------------------------------------

; eval is here
(eval '(+ 1 2))    ; => 3

; plan has no traditional macros or special forms, but function arguments are
; not necessarily evaluated before calling. it is the function's responsibility
; to evaluate arguments. for instance, the built-in function quote returns its
; argument without evaluating it.

; functions created with lambda or define always evaluate their arguments.
; to create one that does not, use flambda
(define lambdax (flambda (e) (lambda (x) (eval e))))
(define f (lambdax (* x 2)))
(f 5)              ; => 10

;;;-----------------------------------------------------------------------------
;;; 7. The rest of it
;;;-----------------------------------------------------------------------------

; Display values with print
(print 'what's_your_name)

; Read values with read
(define name (read))
(print 'hello_there)
(print name)
(print '!)

; Use apply to "unpack" a list into a function's arguments
(apply + '(1 2 3)) ; => 6

; Make a function's parameters an improper list for "rest args" behavior
(define (list . x) x)
(list 1 2 3)       ; => (1 2 3)
repl.plan ASCII text
1
2
3
4
(define (loop)
  (print (eval (read)))
  (loop))
(loop)

round #48

submitted at
0 likes

guesses
comments 1
Sasuke

ughhhhhhhhh


post a comment


moby2.py ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def entry(body, target):
    seconds = []
    matches = 0
    rest = target[2:]
    for i, c in reversed(list(enumerate(body))):
        if c == target[0]:
            cd = (len(body) - i) // (len(target) - 1) + i
            for second in reversed(seconds):
                if second > cd:
                    break
                start = second*2 - i
                step = second - i
                if body[start:second+step*(len(target)-1):step] == rest:
                    return start, step
        if c == target[1]:
            seconds.append(i)

round #47

submitted at
0 likes

guesses
comments 0

post a comment


ans.rs ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use std::env;
use std::process::Command;

static mut ZERO: usize = 0;

fn diff<T: PartialEq + Clone>(x: &[T], y: &[T]) -> Vec<T> {
    let mut table: Vec<usize> = vec![0; x.len() * y.len()]; 
    let mut table_value = |i: usize, j: usize| unsafe {
        if i == usize::MAX || j == usize::MAX {
            &mut ZERO
        } else {
            &mut *(&mut table[j + i*y.len()] as *mut usize)
        }
    };

    for (j, b) in y.iter().enumerate() {
        for (i, a) in x.iter().enumerate() {
            *table_value(i, j) = if a == b {
                *table_value(i-1, j-1) + 1
            } else {
                *table_value(i-1, j).max(table_value(i, j-1))
            };
        }
    }

    let mut r = Vec::new();
    let mut i = x.len() - 1;
    let mut j = y.len() - 1;
    while i != usize::MAX && j != usize::MAX {
        if x[i] == y[j] {
            r.push(x[i].clone());
            i -= 1;
            j -= 1;
        } else if table_value(i, j-1) > table_value(i-1, j) {
            j -= 1;
        } else {
            i -= 1;
        }
    }

    r.reverse();
    r
}

pub fn lis(x: &[u32]) -> Vec<u32> {
    let mut v = x.to_owned();
    v.sort();
    v.dedup();
    diff(x, &v)
}

fn main() -> std::io::Result<()> {
    if cfg!(debug_assertions) {
        // shit.
        Command::new("rustc")
            .arg("-O")
            .arg(file!())
            .spawn()?
            .wait()?;
        Command::new("./ans")
            .args(env::args_os().skip(1))
            .spawn()?
            .wait()?;
        return Ok(());
    }

    let input: Vec<u32> = env::args().skip(1).map(|x| x.parse().unwrap()).collect();
    println!("{:?}", lis(&input));
    Ok(())
}

round #46

submitted at
0 likes

guesses
comments 0

post a comment


new macedonia.py ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from itertools import chain, combinations

def powerset(iterable):
    "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

s=eval(input())
if not s:
    print([()])
l = []
for p in powerset(s[0].keys()):
    if any([set(p)>set(x) for x in l]):
        continue
    ff =[tuple([r[x] for x in p]) for r in s]
    if len(ff)==len(set(ff)):
        l.append(p)
print(l)

round #45

submitted at
0 likes

guesses
comments 0

post a comment


hair.py ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import time
import random
from multiprocessing.shared_memory import ShareableList


def get_or_create(name):
    try:
        return ShareableList(name=name)
    except:
        return ShareableList(sequence=[0] + [b"-"*256]*256, name=name)

def play_random_move(heights):
    o = random.choice([i for i, x in enumerate(heights, start=1) if x != 6])
    heights[o-1] += 1
    print(o)

def play_randomly(go_first):
    """Boring agent used as a fallback when we can't find a partner (me irl)"""
    heights = [0]*7
    if go_first:
        play_random_move(heights)
    while True:
        i = int(input())
        heights[i-1] += 1
        play_random_move(heights)

firsts = get_or_create("cg45_hair_firsts")
seconds = get_or_create("cg45_hair_seconds")

are_first = input() == "f"

theirs, ours = (firsts, seconds) if are_first else (seconds, firsts)

if not theirs[0]:
    channel = ShareableList([None])
    ours[1+ours[0]] = channel.shm.name
    ours[0] += 1
    for _ in range(10):
        time.sleep(0.5)
        if channel[0]:
            break
    else:
        play_randomly(are_first)
    channel[0] = None
else:
    theirs[0] -= 1
    channel = ShareableList(name=theirs[theirs[0]+1])
    channel[0] = 1
    while channel[0] is not None:
        pass

def play_move():
    while channel[0] is None:
        pass
    print(channel[0])
    channel[0] = None

if are_first:
    play_move()
while True:
    channel[0] = int(input())
    while channel[0] is not None:
        pass
    play_move()

round #44

submitted at
0 likes

guesses
comments 0

post a comment


answer.py ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from collections import Counter

OPS = {
    "+": lambda x, y: x + y,
    "-": lambda x, y: x - y if x >= y else None,
    "*": lambda x, y: x * y,
    "/": lambda x, y: x // y if y and not x % y else None,
}

def results(digits, ops):
    for digit in digits:
        yield digit, digits - Counter({digit: 1}), ops
    for op in ops:
        for r1, digits1, ops1 in results(digits, ops - Counter({op: 1})):
            for r2, digits2, ops2 in results(digits1, ops1):
                final = OPS[op](r1, r2)
                if final is not None:
                    yield final, digits2, ops2

def entry(target, bag):
    digits = Counter()
    i = 0
    while i < len(bag) and bag[i].isdigit():
        digits[int(bag[i])] += 1
        i += 1
    ops = Counter(bag[i:])
    return min(results(digits, ops), key=lambda r: abs(r[0] - target))[0]

round #43

submitted at
0 likes

guesses
comments 0

post a comment


core.wfs ASCII text
1
2
3
#!/usr/bin/env -S wolframscript -function -signature Integer Integer Real String -f
(* wrong orientation for the r < 0 case but I don't think it matters *)
({R, r, d, file} |-> Export[file, ParametricPlot[{(R - r) Cos[t] + d Cos[t ((R - r) / r)], (R - r) Sin[t] - d Sin[t ((R - r) / r)]}, {t, 0, 2 Pi (LCM[R, r] / R)}]])

round #42

submitted at
6 likes

guesses
comments 0

post a comment


2048.html ASCII text
ClearSans-Bold-webfont.woff Web Open Font Format, TrueType, length 27120, version 1.0
ClearSans-Regular-webfont.woff Web Open Font Format, TrueType, length 26764, version 1.0

round #41

submitted at
1 like

guesses
comments 0

post a comment


forth.py ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import sys
import math
from lark import Lark
from lark.visitors import Interpreter

l = Lark(r"""
int: /\d+/
char: "'" /./
quote: "[" code "]"
string: /"[^"]*"/
var: /[a-z]/
!simple: ";" | ":" | "$" | "%" | "@" | "\\" | "O" | "+" | "-" | "*" | "/" | "_" | "&" | "|" | "~" | "=" | ">" | "!" | "?" | "#" | "^" | "," | "." | "B"
_inst: int | char | string | quote | simple | var
code: _inst*
%ignore /\s+|{[^}]*}/ 
""", parser="lalr", start="code")

variables = {}
stack = []

class Factor(Interpreter):
    def int(self, tree):
        stack.append(int(tree.children[0]))

    def char(self, tree):
        stack.append(ord(tree.children[0]))

    def quote(self, tree):
        stack.append(lambda: self.visit(tree.children[0]))

    def string(self, tree):
        print(tree.children[0][1:-1], end="")

    def var(self, tree):
        stack.append(tree.children[0].value)

    def simple(self, tree):
        match tree.children[0].value:
            case "$":
                stack.append(stack[-1])
            case "%":
                stack.pop()
            case "\\":
                stack.append(stack.pop(-2))
            case "@":
                stack.append(stack.pop(-3))
            case "O":
                stack.append(stack[-stack.pop()-1])
            case "+":
                stack.append(stack.pop() + stack.pop())
            case "-":
                stack.append(stack.pop(-2) - stack.pop())
            case "*":
                stack.append(stack.pop() * stack.pop())
            case "/":
                stack.append(math.trunc(stack.pop(-2) / stack.pop()))
            case "_":
                stack[-1] *= -1
            case "&":
                stack.append(stack.pop() & stack.pop())
            case "|":
                stack.append(stack.pop() | stack.pop())
            case "~":
                stack.append(~stack.pop())
            case ">":
                stack.append(-1 if stack.pop() < stack.pop() else 0)
            case "=":
                stack.append(-1 if stack.pop() == stack.pop() else 0)
            case "!":
                stack.pop()()
            case "?":
                f = stack.pop()
                if stack.pop():
                    f()
            case "#":
                b = stack.pop()
                c = stack.pop()
                while (c(), stack.pop())[1]:
                    b()
            case ":":
                variables[stack.pop()] = stack.pop(-2)
            case ";":
                stack.append(variables[stack.pop()])
            case "^":
                stack.append(sys.stdin.buffer.read(1)[0] or -1)
            case ",":
                sys.stdout.buffer.write(bytes([stack.pop() % 256]))
            case ".":
                print(stack.pop(), end="")
            case "B":
                sys.stdout.flush()

    def code(self, tree):
        for x in tree.children:
            self.visit(x)

Factor().visit(l.parse(open(sys.argv[1]).read()))

round #40

submitted at
1 like

guesses
comments 0

post a comment


readme.md ASCII text
1
2
shut up shut up shut up shut up shut up
get out of my head get out get out get out
shut_up.rs ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
use std::collections::{BinaryHeap, HashMap};
use std::cmp::Reverse;

type Grid = [[u8; 4]; 4];

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Move {
    Up,
    Down,
    Left,
    Right,
}
use Move::*;

/// didn't miss the cold weather
fn oh_my_god_fuck_you(g: Grid) -> u8 {
    let mut c: u8 = 0;
    for (j, row) in g.iter().enumerate() {
        for (i, &v) in row.iter().enumerate() {
            if v == 0 { continue }
            let v = v as usize - 1;
            let (y, x) = (v / 4, v % 4);
            c += (x.abs_diff(i) + y.abs_diff(j)) as u8;
        }
    }
    c
}

/// didn't miss the sweater weather
fn apply_move(mut g: Grid, m: Move) -> Option<Grid> {
    for j in 0..4 {
        for i in 0..4 {
            if g[j][i] != 0 { continue }
            let (x, y) = match m {
                Up => (i, j + 1),
                Down => (i, j.wrapping_sub(1)),
                Left => (i + 1, j),
                Right => (i.wrapping_sub(1), j),
            };
            if x >= 4 || y >= 4 { return None }
            g[j][i] = g[y][x];
            g[y][x] = 0;
            return Some(g);
        }
    }
    unreachable!()
}

/// I just missed you
pub fn entry(g: Grid) -> Option<Vec<Move>> {
    let mut edge = BinaryHeap::from([Reverse((0, g))]);
    let mut came_from = HashMap::new();
    let mut best = HashMap::new();
    best.insert(g, 0);

    while let Some(Reverse((_, state))) = edge.pop() {
        if oh_my_god_fuck_you(state) == 0 {
            let mut path = Vec::new();
            let mut state = state;
            while let Some(&(m, next)) = came_from.get(&state) {
                path.push(m);
                state = next;
            }
            path.reverse();
            return Some(path);
        }

        for m in [Up, Down, Left, Right].into_iter() {
            let Some(new) = apply_move(state, m) else { continue };
            let new_cost = *best.get(&state).unwrap() + 1;
            if best.get(&new).copied().map(|x| new_cost < x).unwrap_or(true) {
                best.insert(new, new_cost);
                edge.push(Reverse((new_cost + oh_my_god_fuck_you(new), new)));
                came_from.insert(new, (m, state));
            }
        }
    }

    None
}

round #39

submitted at
3 likes

guesses
comments 3
LyricLy

dead link


LyricLy known at the time as [author of #2]

oops its supposed to be https://streamablecom/k2y8fq


LyricLy

👍


post a comment


39.nro data
Makefile magic text fragment for file(1) cmd, 1st line "#---------------------------------------------------------------------------------", 2nd line ".SUFFIXES:"
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------

ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif

TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules

#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
#
# NO_ICON: if set to anything, do not use icon.
# NO_NACP: if set to anything, no .nacp file is generated.
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
# ICON is the filename of the icon (.jpg), relative to the project folder.
#   If not set, it attempts to use one of the following (in this order):
#     - <Project name>.jpg
#     - icon.jpg
#     - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
#   If not set, it attempts to use one of the following (in this order):
#     - <Project name>.json
#     - config.json
#   If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
#   of a homebrew executable (.nro). This is intended to be used for sysmodules.
#   NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET		:=	$(notdir $(CURDIR))
BUILD		:=	build
SOURCES		:=	.
DATA		:=	data
INCLUDES	:=	include
APP_TITLE   :=  cg \#39
APP_AUTHOR  :=  You!
ICON        :=  icon.jpg

#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH	:=	-march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE

CFLAGS	:=	-g -Wall -O2 -ffunction-sections \
			$(ARCH) $(DEFINES)

CFLAGS	+=	$(INCLUDE) -D__SWITCH__

CXXFLAGS	:= $(CFLAGS) -fno-rtti -fno-exceptions

ASFLAGS	:=	-g $(ARCH)
LDFLAGS	=	-specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)

LIBS	:= -lnx

#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS	:= $(PORTLIBS) $(LIBNX)


#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------

export OUTPUT	:=	$(CURDIR)/$(TARGET)
export TOPDIR	:=	$(CURDIR)

export VPATH	:=	$(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
			$(foreach dir,$(DATA),$(CURDIR)/$(dir))

export DEPSDIR	:=	$(CURDIR)/$(BUILD)

CFILES		:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES		:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES	:=	$(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))

#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
	export LD	:=	$(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
	export LD	:=	$(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------

export OFILES_BIN	:=	$(addsuffix .o,$(BINFILES))
export OFILES_SRC	:=	$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES 	:=	$(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN	:=	$(addsuffix .h,$(subst .,_,$(BINFILES)))

export INCLUDE	:=	$(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
			$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
			-I$(CURDIR)/$(BUILD)

export LIBPATHS	:=	$(foreach dir,$(LIBDIRS),-L$(dir)/lib)

ifeq ($(strip $(CONFIG_JSON)),)
	jsons := $(wildcard *.json)
	ifneq (,$(findstring $(TARGET).json,$(jsons)))
		export APP_JSON := $(TOPDIR)/$(TARGET).json
	else
		ifneq (,$(findstring config.json,$(jsons)))
			export APP_JSON := $(TOPDIR)/config.json
		endif
	endif
else
	export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif

ifeq ($(strip $(ICON)),)
	icons := $(wildcard *.jpg)
	ifneq (,$(findstring $(TARGET).jpg,$(icons)))
		export APP_ICON := $(TOPDIR)/$(TARGET).jpg
	else
		ifneq (,$(findstring icon.jpg,$(icons)))
			export APP_ICON := $(TOPDIR)/icon.jpg
		endif
	endif
else
	export APP_ICON := $(TOPDIR)/$(ICON)
endif

ifeq ($(strip $(NO_ICON)),)
	export NROFLAGS += --icon=$(APP_ICON)
endif

ifeq ($(strip $(NO_NACP)),)
	export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
endif

ifneq ($(APP_TITLEID),)
	export NACPFLAGS += --titleid=$(APP_TITLEID)
endif

ifneq ($(ROMFS),)
	export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif

.PHONY: $(BUILD) clean all

#---------------------------------------------------------------------------------
all: $(BUILD)

$(BUILD):
	@[ -d $@ ] || mkdir -p $@
	@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile

#---------------------------------------------------------------------------------
clean:
	@echo clean ...
ifeq ($(strip $(APP_JSON)),)
	@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
	@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif


#---------------------------------------------------------------------------------
else
.PHONY:	all

DEPENDS	:=	$(OFILES:.o=.d)

#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)

all	:	$(OUTPUT).nro

ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro	:	$(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro	:	$(OUTPUT).elf
endif

else

all	:	$(OUTPUT).nsp

$(OUTPUT).nsp	:	$(OUTPUT).nso $(OUTPUT).npdm

$(OUTPUT).nso	:	$(OUTPUT).elf

endif

$(OUTPUT).elf	:	$(OFILES)

$(OFILES_SRC)	: $(HFILES_BIN)

#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o	%_bin.h :	%.bin
#---------------------------------------------------------------------------------
	@echo $(notdir $<)
	@$(bin2o)

-include $(DEPENDS)

#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------
README.txt ASCII text
1
2
install devkitpro packages switch-dev and devkita64, then run make. or use the included .nro
if you have no switch see demonstration.mp4
demonstration.mp4 external link to streamable.com
icon.jpg JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, Exif Standard: [TIFF image data, little-endian, direntries=7, orientation=upper-left, xresolution=98, yresolution=106, resolutionunit=2, software=GIMP 2.10.34, datetime=2023:07:20 20:35:48], progressive, precision 8, 256x256, components 3
interpreter.h ASCII text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
typedef struct {
    enum {
        One,
        Height,
        Pop,
        Toggle,
        Open,
        Push,
        Negate,
        Drop,
        LoopOpen,
        LoopEnd,
    } type;
    size_t target;
} Token;

#define token(x) ((Token) {.type = (x)})

typedef struct {
    Token tokens[PROGRAM_SIZE];
    size_t tokens_size;
} Tokens;

Tokens lex(char *program) {
    Tokens p;
    p.tokens_size = 0;
    #define push_token(x) do { p.tokens[p.tokens_size++] = (x); } while (0)

    size_t open_brackets[PROGRAM_SIZE];
    size_t open_brackets_size = 0;

    for (size_t i = 0; program[i]; i++) {
        switch (program[i]) {
            case '(': case '[': case '<':
                push_token(token(Open));
                break;
            #define match_last(x, a, b) if (p.tokens_size && p.tokens[p.tokens_size-1].type == Open) p.tokens[p.tokens_size-1] = token(a); else push_token(token(b));
            case ')': match_last('(', One, Push); break;
            case ']': match_last('[', Height, Negate); break;
            case '>': match_last('<', Toggle, Drop); break;
            case '{':
                open_brackets[open_brackets_size++] = p.tokens_size;
                push_token(token(LoopOpen));
                break;
            case '}':
                open_brackets_size--;
                if (p.tokens_size && p.tokens[p.tokens_size-1].type == LoopOpen) {
                    p.tokens[p.tokens_size-1] = token(Pop);
                } else {
                    size_t idx = open_brackets[open_brackets_size];
                    p.tokens[idx].target = p.tokens_size;
                    push_token(((Token) {.type = LoopEnd, .target = idx}));
                }
                break;
        }
    }

    return p;
}

Stack execute(Stack active, char *program) {
    Tokens p = lex(program);
    Stack inactive = {0};
    Stack third = {0};
    stack_push(&third, 0);

    for (size_t i = 0; i < p.tokens_size; i++) {
        Token t = p.tokens[i];
        switch (t.type) {
            case One:
                (*stack_peek(&third))++;
                break;
            case Height:
                *stack_peek(&third) += active.size;
                break;
            case Pop:
                *stack_peek(&third) += active.size ? stack_pop(&active) : 0;
                break;
            case Toggle:
                Stack tmp = active;
                active = inactive;
                inactive = tmp;
                break;
            case Open:
                stack_push(&third, 0);
                break;
            case Push: {
                int64_t x = stack_pop(&third);
                *stack_peek(&third) += x;
                stack_push(&active, x);
                break;
            }
            case Negate: {
                int64_t x = stack_pop(&third);
                *stack_peek(&third) -= x;
                break;
            }
            case Drop:
                stack_pop(&third);
                break;
            case LoopOpen:
                if (!active.size || !*stack_peek(&active)) i = t.target;
                break;
            case LoopEnd:
                if (active.size && *stack_peek(&active)) i = t.target;
                break;
        }
    }

    stack_destroy(&inactive);
    stack_destroy(&third);
    return active;
}
main.c ASCII text
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#define PROGRAM_SIZE 500
#define INPUT_SIZE 10

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <inttypes.h>
#include <switch.h>
#include "stack.h"
#include "interpreter.h"

#define error(s, kind) do { strncpy(string, (s), string_size); return SwkbdTextCheckResult_##kind; } while (0)

SwkbdTextCheckResult is_balanced(char* string, size_t string_size) {
    char stack[500];
    size_t height = 0;
    bool non_bracket = false;

    for (size_t i = 0; string[i]; i++) {
        char c = string[i];
        switch (string[i]) {
            case '<': case '{': case '[': case '(':
                stack[height++] = c;
                break;

            #define ensure(x) if (!height) error("Unexpected close bracket.", Bad); else if (stack[--height] != (x)) error("Brackets are mismatched.", Bad); else {}
            case '>': ensure('<'); break;
            case ']': ensure('['); break;
            case ')': ensure('('); break;
            case '}': ensure('{'); break;

            case ' ': case '\n': break;
            default: non_bracket = true;
        } 
    }

    if (height) error("Unclosed bracket.", Bad);
    if (non_bracket) error("Some characters are not brackets and will be ignored.", Prompt);

    return SwkbdTextCheckResult_OK;
}

void query_program(char *program) {
    SwkbdConfig kbd;
    Result rc = swkbdCreate(&kbd, 0);
    if (!R_SUCCEEDED(rc)) return;
    swkbdConfigMakePresetDefault(&kbd);

    swkbdConfigSetInitialText(&kbd, program);
    swkbdConfigSetGuideText(&kbd, "Program (sorry, it only lets me do 500 characters...)");
    swkbdConfigSetTextCheckCallback(&kbd, is_balanced);

    char out[PROGRAM_SIZE+1] = {0};
    rc = swkbdShow(&kbd, out, sizeof out);

    if (R_SUCCEEDED(rc)) {
        strcpy(program, out);
    }

    swkbdClose(&kbd);
}

void query_input(int64_t *n) {
    SwkbdConfig kbd;
    Result rc = swkbdCreate(&kbd, 0);
    if (!R_SUCCEEDED(rc)) return;
    swkbdConfigMakePresetDefault(&kbd);

    char initial[INPUT_SIZE+1];
    snprintf(initial, sizeof initial, "%" PRId64, *n);
    swkbdConfigSetInitialText(&kbd, initial);

    swkbdConfigSetType(&kbd, SwkbdType_NumPad);
    swkbdConfigSetHeaderText(&kbd, "Enter an input value.");
    swkbdConfigSetStringLenMax(&kbd, INPUT_SIZE);
    swkbdConfigSetTextDrawType(&kbd, SwkbdTextDrawType_Line);

    char out[INPUT_SIZE+1];
    rc = swkbdShow(&kbd, out, sizeof out);

    if (R_SUCCEEDED(rc) && *out) {
        *n = atoll(out);
    }

    swkbdClose(&kbd);
}

typedef enum {
    Input,
    Program,
    Execute,
} Selected;

typedef struct {
    Stack input;
    Stack output;
    size_t selected_input;
    Selected selected;
    char program[PROGRAM_SIZE+1];
} InputState;

void clear(void) {
    printf("\033[2J");
}

void cyan(void) {
    printf("\033[36;1m");
}

void green(void) {
    printf("\033[32;1m");
}

void reset(void) {
    printf("\033[0m");
}

void print_state(InputState *state) {
    clear();
    printf("Move to select\nA to edit value\nY to push input\nX to pop input\n+ to exit\n\nInputs: ");

    for (size_t i = 0; i < state->input.size; i++) {
        if (state->selected == Input && i == state->selected_input) cyan();
        printf("%" PRId64 " ", state->input.values[i]);
        reset();
    }

    if (state->selected == Program) cyan();
    printf("\nProgram:\n%s\n\n", state->program);
    reset();

    if (state->selected == Execute) cyan();
    printf("Execute\n");
    reset();

    green();
    for (size_t i = 0; i < state->output.size; i++) {
        printf("%" PRId64 " ", state->output.values[i]);
    }
    reset();

    consoleUpdate(NULL);
}

void correct_selected(InputState *state) {
    consoleUpdate(NULL);
    Selected min = state->input.size ? Input : Program;
    Selected max = Execute;
    if (state->selected < min) state->selected = min;
    if (state->selected > max) state->selected = max;
}

int main(void) {
    consoleInit(NULL);
    padConfigureInput(1, HidNpadStyleSet_NpadStandard);
    PadState pad;
    padInitializeDefault(&pad);

    InputState state = {.selected = Program};
    print_state(&state);

    while (appletMainLoop()) {
        padUpdate(&pad);

        u64 down = padGetButtonsDown(&pad);

        if (down & HidNpadButton_Plus) break;
        if (down & HidNpadButton_A) {
            switch (state.selected) {
                case Input:
                    query_input(&state.input.values[state.selected_input]);
                    break;
                case Program:
                    query_program(state.program);
                    break;
                case Execute:
                    stack_destroy(&state.output);
                    state.output = execute(stack_clone(&state.input), state.program);
                    break;
            }
        }
        if (down & HidNpadButton_Y) stack_push(&state.input, 0);
        if (down & HidNpadButton_X && state.input.size) stack_pop(&state.input);
        if (down & HidNpadButton_AnyUp) {
            state.selected--;
            correct_selected(&state);
        }
        if (down & HidNpadButton_AnyDown) {
            state.selected++;
            correct_selected(&state);
        }
        if (state.selected == Input) {
            if (down & HidNpadButton_AnyLeft && state.selected_input) state.selected_input--;
            if (down & HidNpadButton_AnyRight) state.selected_input++;
            if (state.selected_input >= state.input.size) state.selected_input = state.input.size-1;
        }
        if (down) print_state(&state);
    }

    stack_destroy(&state.input);
    stack_destroy(&state.output);
    consoleExit(NULL);
}
stack.h ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdint.h>
#include <malloc.h>
#include <string.h>

typedef struct {
    int64_t *values;
    size_t size;
    size_t capacity;
} Stack;

int64_t *stack_push(Stack *s, int64_t val) {
    if (s->size == s->capacity) {
        s->capacity = s->capacity ? s->capacity * 2 : 64;
        s->values = realloc(s->values, s->capacity * sizeof (int64_t));
    }
    int64_t *p = &s->values[s->size++];
    *p = val;
    return p;
}

int64_t *stack_peek(Stack *s) {
    return &s->values[s->size-1];
}

int64_t stack_pop(Stack *s) {
    return s->values[--s->size];
}

Stack stack_clone(Stack *s) {
    Stack c = *s;
    c.values = malloc(c.capacity * sizeof (int64_t));
    memcpy(c.values, s->values, c.size * sizeof (int64_t));
    return c;
}

void stack_destroy(Stack *s) {
    free(s->values);
}

round #38

submitted at
1 like

guesses
comments 0

post a comment


lol.sh ASCII text
1
2
3
4
5
x=$(sed 's/*/ */g;s/\// \//g' $1)
while [[ "$x" != ?(-)+([0-9]) ]];do
x=$(xargs -I{} -d' ' -n1 bash -c '{ echo "{}";echo "{}"|bc 2>/dev/null;}|tail -n1|tr "\n" " "'<<<"$x "|sed -r 's/ $//;s/(.) /\1/g')
done
echo "$x"

round #37

submitted at
1 like

guesses
comments 0

post a comment


main.ysl ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
%include "std.ysl"

func true
    return 1
endfunc

func isdigit x
    param x
    if greater $x 0x2F
        less $x 0x3A
    else
        return 0
    endif
endfunc

func readint
    local int out
    set out 0
    local int input

    while true
        getch
        to input

        if equals $input 13
            putch 13
            putch 10
            return $out
        endif

        if isdigit $input
            putch $input
            var_mul &out 10
            var_add &out $input
            var_sub &out !0
        endif
    endwhile
endfunc

func main
    local int i

    local string numMsg "Number (in base 10): "
    putstr &numMsg
    local int num
    readint
    to num

    local string baseMsg "Radix: "
    putstr &baseMsg
    local int base
    readint
    to base

    # funny cases
    if equals $base 0
        local string undefinedMsg "undefined"
        putstr &undefinedMsg
        return
    endif
    if equals $base 1
        for i $num
            putch !1
            putch 32
        endfor
        return
    endif

    while true
        if less $num $base
            putdec $num
            return
        endif

        local int sum
        set sum 0
        while greater $num 0
            mod $num $base
            to i
            var_add &sum $i
            var_div &num $base
        endwhile

        set num $sum
    endwhile
endfunc

round #36

submitted at
1 like

guesses
comments 3
Olive

:3



essaie

Cute£


post a comment


sss.py ASCII text, with very long lines (500)
1
2
3
4
5
6
7
def entry(n, x, y):
 # put all the money in a big pile                                                                                                                                                                                                                                                      # solve it
   l = x + y                                                                                                                                                                                                                                                                             ; p = n + sum(y); r = []; o = y[:]; [l.remove(e := __import__("random").choice(l)) or (o.remove(e) if e in o else r.append(e)) or (p := p - e) for _ in iter(lambda: p > 0, False)]; return entry(n, x, y) if p else (r, o)
 # burn it
   del l[:]
 # return ashes
   return l, l

round #35

submitted at
0 likes

guesses
comments 0

post a comment


outoftime.py ASCII text, with CRLF line terminators
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def shunt(fuck, me, sideways, where):
    x, y = where[0] + sideways[0], where[1] + sideways[1]
    if fuck[y][x] > me:
        return False
    if fuck[y][x] and not shunt(fuck, me, (x, y), where):
         return False
    fuck[y][x], fuck[where[1]][where[0]] = fuck[where[1]][where[0]], fuck[y][x]
    return True

def epic(fuck, help):
   for j, y in enumerate(fuck):
       for i, x in enumerate(y):
            if x < 0:
                shunt(fuck, abs(x), (i, j), (0, -1) if help == "W" else (-1, 0) if help == "A" else (0, 1) if help == "S" else (1, 0))
                return

def entry(fuck, shit):
    for help in shit:
        epic(fuck, help)
    return fuck

round #34

submitted at
4 likes

guesses
comments 12
LyricLy known at the time as [author of #2]

welcome to the fizzbuzz golf. please comment your fizzbuzz solution in python on this entry to have the chance to be a part of cg history. my algorithm will run your solution and credit you for having the shortest code.


olus2000 known at the time as [author of #14]

print("FizzBuzz")


LyricLy

print(*["Fizz"*(n%3<1)+"Buzz"*(n%5<1)or n for n in range(1,101)],sep='\n')


olus2000

[print("Fizz"*(n%3<1)+"Buzz"*(n%5<1)or n)for n in range(1,101)]


LyricLy

for n in range(1,101):print("Fizz"*(n%3<1)+"Buzz"*(n%5<1)or n)


olus2000

print(1)


olus2000

print()


Edgex42 known at the time as [author of #6]

{(‘FizzBuzz’ ‘Fizz’ ‘Buzz’,⍵)[(0=15 3 5|⍵)⍳1]}¨⍳100


nurdle

for(i=1;i<101;i++)alert(i%15?i%5?i%3?i:"fizz":"buzz":"fizzbuzz");


nurdle

for(i=1;i<101;i++)alert((i%3?'':'fizz'+(i%5?'':'buzz'))|i);


soup girl

print(code := ""); fizz = "1\n2\nuhh, Fizz, right?\nand after that is 4\nthen 5\nand Fizz again wait shit was 5 supposed to be buzz? ok ignore that\n7\n8\nFizz\nBuzz is there even any point going on? i'll start over\n1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n16\n17 it's all just the same after 15 isn't it, may as well stop here"


LyricLy known at the time as [author of #2]

i should have flipped the parameters to removeprefix


post a comment


golf.py ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import io
import requests
import sys
from bs4 import BeautifulSoup
from contextlib import redirect_stdout

fizz = "\n".join(f"{n:02}" for n in range(1, 101))
for n in range(0, 101, 3):
    fizz = fizz.replace(f"{n:02}\n", f"Fizz{n:02}\n")
for n in range(0, 101, 5):
    fizz = fizz.replace(f"{n:02}\n", f"Buzz\n")
while (new_fizz := fizz.replace("\n0", "\n")) and [(new_fizz := new_fizz.replace(f"z{n}", f"z")) for n in range(10)][-1] != fizz and (fizz := new_fizz): pass
fizz = fizz[1:].replace("100", "Buzz")

t = requests.get("https://cg.esolangs.gay/34/").text
soup = BeautifulSoup(t, "lxml")

comments = [(e.text, e.next_sibling.next_sibling.next_sibling.text.strip(" \n`")) for e in soup.find("a", string="golf.py").parent.parent.previous_sibling.previous_sibling.find_all("strong")[1:]]
comments.sort(key=lambda x: len(x[1]))

for author, code in comments:
    try:
        with redirect_stdout(io.StringIO()) as f:
            exec(code, None, None)
        int(f.getvalue().removesuffix("\n").removeprefix(fizz) + "0"*f.tell())
    except Exception as e:
        continue
    print(f"{author}: {len(code.encode())} bytes", file=sys.stderr)
    break

print(fizz)

round #33

submitted at
0 likes

guesses
comments 0

post a comment


cg.esolangs.gay ASCII text
1
2
3
4
5
p,n=eval(input())
while 1:
 for f in p:
  if not n*f%1:n*=f;break
 else:break
doc ASCII text
1
2
3
4
provide list of fractions and input number
fractions must be imported:
[(f := __import__("fractions").Fraction)(17, 91), f(78, 85), f(19, 51)], 2
observe output via halting state

round #32

submitted at
3 likes

guesses
comments 0

post a comment


WilsonMazeGen.gif GIF image data, version 89a, 240 x 240
gen ASCII text
1
#!/usr/bin/cat WilsonMazeGen.gif

round #31

submitted at
3 likes

guesses
comments 0

post a comment


entry.bbj.xor data
pals.hpp ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#ifndef PALS
#define PALS

#include <algorithm>
#include <fstream>
#include <unordered_set>
#include <vector>

using namespace std;

vector<vector<size_t>> solve(vector<unordered_set<size_t>> const& week, size_t i, unordered_set<size_t> const& friends) {
    if (week.size() <= i)
        return {{}};

    vector<vector<size_t>> result;

    for (auto n : friends) {
        if (week[i].count(n)) {
            auto new_friends = friends;
            new_friends.erase(n);
            auto solution = solve(week, i+1, new_friends);
            for (auto& r : solution) {
                r.push_back(n);
            }
            result.insert(result.end(), solution.begin(), solution.end());
        }
    }

    return result;
}

vector<size_t> fallback_entry(vector<unordered_set<size_t>> week) {
    unordered_set<size_t> friends;
    for (size_t i = 0; i < week.size(); i++) {
        friends.insert(i);
    }
    auto v = solve(week, 0, friends)[0];
    reverse(v.begin(), v.end());
    return v;
}

vector<size_t> entry(vector<unordered_set<size_t>> week) {
    ifstream input("entry.bbj.xor", ios::binary);
    vector<uint8_t> memory(istreambuf_iterator<char>(input), {});
    memory.resize(100000);

    uint8_t x = 57;
    for (auto& c : memory) {
        c ^= x;
        x = 45 * x + 7;
    }

    for (uint8_t i = 0; i < week.size(); i++) {
        memory[0x8F00 | i] = memory.size();
        memory.insert(memory.end(), week[i].begin(), week[i].end());
    }

    uint32_t ip = 0;
    while (ip % 2 - 1 != UINT32_MAX) {
        uint32_t x = memory[ip++];
        x |= memory[ip++] << 8;
        x |= memory[ip++] << 16;
        x |= memory[ip++] << 24;
        uint32_t y = memory[ip++];
        y |= memory[ip++] << 8;
        y |= memory[ip++] << 16;
        y |= memory[ip++] << 24;
        uint32_t z = memory[ip++];
        z |= memory[ip++] << 8;
        z |= memory[ip++] << 16;
        z |= memory[ip] << 24;
        memory[y % 100000] = memory[x % 100000];
        ip = z % 9975;
    }

    vector<size_t> output;
    for (uint8_t i = 0;; i++) {
        auto byte = memory[0x9F00 | i];
        if (byte == 0xFF)
            break;
        output.push_back(byte);
    }

    // doesnt work if there are more than 254 friends
    auto fallback = fallback_entry(week);
    if (fallback != output)
        return fallback;

    return output;
}

#endif

round #30

submitted at
3 likes

guesses
comments 3
olus2000 known at the time as [author of #6]

Line 95 should read: t = random.choice([x[0] for x in cs])


LyricLy known at the time as [author of #8] replying to olus2000

I don't think it should.


LyricLy known at the time as [author of #8] replying to olus2000

Oh! You mean line 95, not 98. Yes, that's right. How do I ping someone with this?


post a comment


game.py ASCII text, with very long lines (690)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#          Distance the Wumpus
# a spiritual successor to the hit 1973 game
#
# Now the Wumpus is hunting YOU!
# You have lost your trusty bow and crooked arrows, and must traverse the caves to find it, avoiding familiar hazards, to put down the Wumpus once and for all.
# Be careful, the cave system has changed...
#
# Play by invoking `entry` as usual. Alternatively, you can run the game from the command line by passing the strings as arguments: `python3 game.py kitten sitting`


def entry(x, y):
    # The source code has been indented to hide it from view. Scroll right to see it, but you may be spoiled on the game's mechanics.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            import random

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            DOWN = 0, 1
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            RIGHT = 1, 0
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            DOWNRIGHT = 1, 1
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            BATS = 0
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            PIT = 1

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            def find_paths(x, y):
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                d = {(-1, -1): ({(-1, -1)}, 0)}

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                for i in range(len(x)):
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    d[i, -1] = {(x, -1) for x in range(i)}, i+1
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                for j in range(len(y)):
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    d[-1, j] = {(-1, y) for y in range(j)}, j+1

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    for i in range(len(x)):    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        rp, rc = d[i-1, j]
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        dp, dc = d[i, j-1]
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        drp, drc = d[i-1, j-1]

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        tries = [
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            (rp | {(i, j)}, rc + 1),
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            (dp | {(i, j)}, dc + 1),
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           (drp | {(i, j)}, drc + (x[i] != y[j])),
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        ]

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        best = min(map(lambda k: k[1], tries))
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        paths = set()
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        for trie, c in tries:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            if c == best:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                paths |= trie
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        d[i, j] = (paths, best)

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                return d[len(x)-1, len(y)-1] 

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            def pick_colour(d=None):
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                if d == DOWN:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    l = "A red", "An orange", "An auburn", "A cherry", "An amber", "A scarlet"
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                elif d == RIGHT:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    l = "A turquoise", "A cyan", "A teal", "A blue", "An aqua", "An azure"
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                elif d == DOWNRIGHT:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    l = "A purple", "A pink", "A magenta", "A violet", "A lavender", "A lilac"
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                elif d is None:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    l = "A black", "A grey", "A gray", "A white", "A plain", "An unmarked", "A silver"
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                return random.choice(l)

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            def in_range(p):
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                return -1 <= p[0] < len(x) and -1 <= p[1] < len(y)

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            def get_connections(p, *, use_current=True):
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                candidates = [(c, pick_colour((dx, dy))) for dx, dy in [DOWN, RIGHT, DOWNRIGHT] if in_range(c := (p[0] + dx, p[1] + dy)) and c not in connections]
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                random.shuffle(candidates)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                if use_current:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    if len(candidates) > 2:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        for i in range(len(candidates)):
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            if candidates[i][0] not in trie:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                candidates.pop(i)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                break
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        else:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            candidates.pop()
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    candidates.append((current_room, pick_colour()))
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                for candidate, _ in candidates:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    if candidate not in numbers:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        numbers[candidate] = room_numbers.pop()
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                return candidates

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            def get_location(prompt, cs, *, validate):
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                while True:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    try:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        n = int(input(prompt))
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    except ValueError:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        print("Enter a number")
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        continue
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    for t, _ in cs:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        if numbers[t] == n:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            break
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    else:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        if validate:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            print("No tunnel leading there")
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            continue
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        else:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            t = random.choice(cs)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    break
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                if t not in connections:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    connections[t] = get_connections(t)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                return t

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            trie, answer = find_paths(x, y)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            if min(len(x), len(y)) < 2:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                print("Please provide strings of length greater than 2 to play.")
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                return answer

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            room_numbers = list(range(1, (len(x)+1)*(len(y)+1) + 1))
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            random.shuffle(room_numbers)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            numbers = {(-1, -1): room_numbers.pop()}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            connections = {}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            connections[(-1, -1)] = get_connections((-1, -1), use_current=False)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            current_room = -1, -1
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            prowling = False
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            bow = False
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            arrows = 5

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            hazards = {}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            for j in range(-1, len(y)):
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                for i in range(-1, len(x)):
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    if (i, j) == (-1, -1) or (i, j) in trie:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        continue
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    m = 1 - abs(i-j) / max(len(x), len(y)) / 2
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    r = random.random()
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    if r < 0.125*m:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        hazards[i, j] = BATS
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    elif r < 0.25*m:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        hazards[i, j] = PIT

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            while True:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                wumpus = random.randrange(-1, len(x)), random.randrange(-1, len(y))
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                if wumpus == (-1, -1) or wumpus in hazards or wumpus in trie: