all stats

luatic's stats

guessed the most

namecorrect guessesgames togetherratio
seshoumara441.000
yui340.750
kimapr11160.688
Palaiologos480.500
Olivia6120.500
kotnen490.444
olus20007160.438
LyricLy9210.429
vspf250.400
moshikoi280.250
olive140.250
essaie140.250
taswelll3130.231
Dolphy290.222
soup girl160.167
razetime1100.100
JJRubes050.000
IFcoltransG040.000
theqwertiest040.000

were guessed the most by

namecorrect guessesgames togetherratio
yui240.500
kimapr8160.500
olive240.500
Dolphy490.444
moshikoi370.429
taswelll5130.385
LyricLy8210.381
kotnen380.375
Palaiologos260.333
essaie140.250
olus20004160.250
razetime290.222
vspf150.200
JJRubes150.200
Olivia2120.167
soup girl040.000
seshoumara040.000
theqwertiest040.000

entries

round #62

submitted at
2 likes

guesses
comments 0

post a comment


dir sped
dir src
dir e
josh.zig ASCII text, with very long lines (65536), with no line terminators
oom.zig 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
// https://www.gamers.org/~fpv/doomlogo.html
pub const oom =
    \\                      ===============     ===============   ========  ========
    \\                     //. . . . . . .\\   //. . . . . . .\\  \\. . .\\// . . //
    \\                    ||. . ._____. . .|| ||. . ._____. . .|| || . . .\/ . . .||
    \\                    || . .||   ||. . || || . .||   ||. . || ||. . . . . . . ||
    \\                    ||. . ||   || . .|| ||. . ||   || . .|| || . | . . . . .||
    \\                    ||-_ .||   ||. . || || . .||   ||. _-|| ||-_.|\ . . . . ||
    \\                    ||  `-||   || . .|| ||. . ||   ||-'  || ||  `|\_ . .|. .||
    \\                    ||    ||   ||_ . || || . _||   ||    || ||   |\ `-_/| . ||
    \\                    ||    \|.  || `-_|| ||_-' ||  .|/    || ||   | \  / |-_.||
    \\                    ||      `-_||    || ||    ||_-'      || ||   | \  / |  `||
    \\                    ||         `'    || ||    `'         || ||   | \  / |   ||
    \\                    `===.         .==='.`===.         .===' /==. |  \/  |   ||
    \\                    |-_ `===. .==='   _|_   `===. .===' _-|/   `==  \/  |   ||
    \\                       `-_  `='    _-'   `-_    `='  _-'   `-_  /|  \/  |   ||
    \\                          `-__\._-'         `-_./__-'         `' |. /|  |   ||
    \\                                                                  `' |  /==.||
    \\                                                                      \/   `==
    \\                                                                       `-_   /
    \\                                                                          ``'
    \\
;
dir reg
dfa.zig 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
const std = @import("std");
const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayListUnmanaged;
const DynamicBitSet = std.bit_set.DynamicBitSet;

const AutoHashMap = std.AutoArrayHashMapUnmanaged;
fn CharMap(V: type) type {
    return AutoHashMap(u8, V);
}

pub fn DFA(V: type) type {
    const State = struct {
        const Self = @This();
        out: CharMap(usize),
        accepting: bool,
        value: V,
        pub fn init(allocator: Allocator, accepting: bool, value: V) !Self {
            return .{
                .out = try AutoHashMap(u8, usize).init(allocator, &.{}, &.{}),
                .accepting = accepting,
                .value = value,
            };
        }
        pub fn deinit(self: *Self, allocator: Allocator) void {
            self.out.deinit(allocator);
        }
    };
    return struct {
        const Self = @This();
        states: ArrayList(State),
        allocator: Allocator,
        pub fn init(allocator: Allocator) Self {
            return .{
                .states = ArrayList(State).initBuffer(&.{}),
                .allocator = allocator,
            };
        }
        pub fn deinit(self: *Self) void {
            for (self.states.items) |*state|
                state.deinit(self.allocator);
            self.states.deinit(self.allocator);
        }
        pub fn addState(self: *Self, accepting: bool, value: V) !usize {
            try self.states.append(self.allocator, try State.init(self.allocator, accepting, value));
            return self.states.items.len - 1;
        }
        pub fn addTransition(self: *Self, from: usize, via: u8, to: usize) !void {
            try self.states.items[from].out.put(self.allocator, via, to);
        }
        pub fn getTransition(self: Self, from: usize, via: u8) ?usize {
            return self.states.items[from].out.get(via);
        }
        pub fn accepts(self: Self, input: []const u8) bool {
            var state: usize = 0;
            for (input) |c| {
                if (self.getTransition(state, c)) |next_state|
                    state = next_state
                else
                    return false;
            }
            return self.states.items[state].accepting;
        }
    };
}
ex.zig 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
const std = @import("std");
const Allocator = std.mem.Allocator;
const NFAC = @import("nfa.zig").NFA;
const DFAC = @import("dfa.zig").DFA;
const CharSet = std.bit_set.IntegerBitSet(256);

pub fn RegexReader(V: type) type {
    return struct {
        const NFA = NFAC(V);
        const DFA = DFAC(V);
        const Self = @This();
        pub const SyntaxError = error{
            UnexpectedEOF,
            UnclosedParenthesis,
            ExpectedOperand,
            UnexpectedChar,
            InvalidEscape,
        };
        pub const ReadError = SyntaxError || Allocator.Error;
        nfa: *NFA,
        i: usize,
        buf: []const u8,
        pub fn init(nfa: *NFA, expr: []const u8) Self {
            return .{
                .nfa = nfa,
                .i = 0,
                .buf = expr,
            };
        }
        fn getch(self: *Self) ?u8 {
            if (self.eof()) return null;
            const c = self.buf[self.i];
            self.i += 1;
            return c;
        }
        fn getch2(self: *Self) ReadError!u8 {
            return self.getch() orelse return SyntaxError.UnexpectedEOF;
        }
        fn ungetch(self: *Self) void {
            self.i -= 1;
        }
        fn eof(self: Self) bool {
            return self.i >= self.buf.len;
        }
        fn readEscape(self: *Self) ReadError!u8 {
            return switch (try self.getch2()) {
                'x' => l: {
                    const hi = try self.getch2();
                    const lo = try self.getch2();
                    break :l std.fmt.parseUnsigned(u8, &.{ hi, lo }, 16) catch |e| switch (e) {
                        error.Overflow => unreachable,
                        error.InvalidCharacter => return SyntaxError.InvalidEscape,
                    };
                },
                else => |c| c,
            };
        }
        fn readAtom(self: *Self) ReadError!?NFA.Sub {
            if (self.eof()) return null;
            switch (self.getch().?) {
                '(' => {
                    const res = try self.readChoice();
                    return if (try self.getch2() != ')') SyntaxError.UnclosedParenthesis else res;
                },
                '%' => return try self.nfa.str(&.{try self.readEscape()}),
                '[' => {
                    var charset = CharSet.initEmpty();
                    const complement = try self.getch2() == '^';
                    if (!complement) self.ungetch();
                    while (true) {
                        var c = try self.getch2();
                        if (c == ']') break;
                        if (c == '%')
                            c = try self.readEscape();
                        const c2 = try self.getch2();
                        if (c2 == '-') {
                            const c3 = try self.getch2();
                            if (c > c3) continue; // empty range
                            charset.setRangeValue(.{ .start = c, .end = c3 }, true);
                        } else {
                            charset.set(c);
                            self.ungetch();
                        }
                    }
                    if (complement) charset = charset.complement();
                    return try self.nfa.charset(charset);
                },
                ')', '?', '+', '*', '|' => {
                    self.ungetch();
                    return null;
                },
                else => |c| return try self.nfa.str(&.{c}),
            }
        }
        fn readQuantified(self: *Self) ReadError!?NFA.Sub {
            const base = try self.readAtom() orelse return null;
            return try switch (self.getch() orelse return base) {
                '?' => self.nfa.opt(base),
                '+' => self.nfa.rep(base),
                '*' => self.nfa.kleene(base),
                else => {
                    self.ungetch();
                    return base;
                },
            };
        }
        fn readConcat(self: *Self) ReadError!NFA.Sub {
            var base = try self.readQuantified() orelse return SyntaxError.ExpectedOperand;
            while (try self.readQuantified()) |q| base = try self.nfa.concat(&.{ base, q });
            return base;
        }
        fn readChoice(self: *Self) ReadError!NFA.Sub {
            var base = try self.readConcat();
            while (true) {
                if (self.getch()) |c| {
                    if (c == '|') base = try self.nfa.choice(&.{ base, try self.readConcat() }) else {
                        self.ungetch();
                        return base;
                    }
                } else return base;
            }
        }
        pub fn read(self: *Self) !NFA.Sub {
            const res = try self.readChoice();
            if (!self.eof())
                return error.UnexpectedChar;
            return res;
        }
    };
}

fn nothing(_: void, _: void) void {
    return {};
}

pub fn regexToDFA(allocator: Allocator, regex: []const u8) !DFAC(void) {
    var nfa = NFAC(void).init(allocator, {});
    defer nfa.deinit();
    var rr = RegexReader(void).init(&nfa, regex);
    const sub = try rr.read();
    return nfa.toDFA(sub, nothing);
}
nfa.zig 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
const std = @import("std");
const Allocator = std.mem.Allocator;
const CharSet = std.bit_set.IntegerBitSet(256);
fn HashMap(K: type, V: type) type {
    return std.ArrayHashMapUnmanaged(K, V, struct {
        pub fn hash(_: @This(), k: K) u32 {
            var hasher = std.hash.Wyhash.init(1233543646);
            std.hash.autoHashStrat(&hasher, k, .DeepRecursive);
            return @truncate(hasher.final());
        }
        pub fn eql(_: anytype, k1: K, k2: K, _: anytype) bool {
            return std.mem.eql(@typeInfo(K).Pointer.child, k1, k2);
        }
    }, true);
}
const AutoHashMap = std.AutoArrayHashMapUnmanaged;
const ArrayList = std.ArrayListUnmanaged;

const DFA = @import("dfa.zig").DFA;

const Subset = []const usize;

fn orderUsize(_: void, a: usize, b: usize) std.math.Order {
    return std.math.order(a, b);
}

fn contains(set: Subset, end_state: usize) bool {
    return std.sort.binarySearch(usize, end_state, set, {}, orderUsize) != null;
}

pub fn NFA(V: type) type {
    const Transition = struct { char: ?u8, to: usize };

    const State = struct {
        const Self = @This();

        out: ArrayList(Transition),
        value: V,
        pub fn init(value: V) Self {
            return .{
                .out = ArrayList(Transition).initBuffer(&.{}),
                .value = value,
            };
        }
        pub fn deinit(self: *Self, allocator: Allocator) void {
            self.out.deinit(allocator);
        }
    };

    return struct {
        const Self = @This();

        pub const Sub = struct {
            start: usize,
            end: usize,
        };

        allocator: Allocator,
        states: ArrayList(State),
        value: V, // HACK this is lazy

        pub fn init(allocator: Allocator, value: V) Self {
            return .{
                .allocator = allocator,
                .states = ArrayList(State).initBuffer(&.{}),
                .value = value,
            };
        }
        pub fn deinit(self: *Self) void {
            for (self.states.items) |*state|
                state.deinit(self.allocator);
            self.states.deinit(self.allocator);
        }
        pub fn addState(self: *Self) Allocator.Error!usize {
            try self.states.append(self.allocator, State.init(self.value));
            return self.states.items.len - 1;
        }
        pub fn addTransition(self: *Self, from: usize, char: ?u8, to: usize) Allocator.Error!void {
            try self.states.items[from].out.append(self.allocator, .{ .char = char, .to = to });
        }
        pub fn rep(self: *Self, sub: Sub) Allocator.Error!Sub {
            try self.addTransition(sub.end, null, sub.start);
            return sub;
        }
        pub fn opt(self: *Self, sub: Sub) Allocator.Error!Sub {
            try self.addTransition(sub.start, null, sub.end);
            return sub;
        }
        pub fn kleene(self: *Self, sub: Sub) Allocator.Error!Sub {
            return self.rep(try self.opt(sub));
        }
        pub fn choice(self: *Self, subs: []const Sub) Allocator.Error!Sub {
            const new = Sub{ .start = try self.addState(), .end = try self.addState() };
            for (subs) |sub| {
                try self.addTransition(new.start, null, sub.start);
                try self.addTransition(sub.end, null, new.end);
            }
            return new;
        }
        pub fn concat(self: *Self, subs: []const Sub) Allocator.Error!Sub {
            var last_end = subs[0].end;
            for (subs[1..]) |sub| {
                try self.addTransition(last_end, null, sub.start);
                last_end = sub.end;
            }
            return .{ .start = subs[0].start, .end = last_end };
        }
        pub fn str(self: *Self, s: []const u8) Allocator.Error!Sub {
            const start = try self.addState();
            var end = start;
            for (s) |c| {
                const new_end = try self.addState();
                try self.addTransition(end, c, new_end);
                end = new_end;
            }
            return .{ .start = start, .end = end };
        }
        pub fn charset(self: *Self, set: CharSet) Allocator.Error!Sub {
            const start = try self.addState();
            const end = try self.addState();
            var chars = set.iterator(.{});
            while (chars.next()) |c|
                try self.addTransition(start, @intCast(c), end);
            return .{ .start = start, .end = end };
        }

        fn hull(self: Self, states: Subset, hull_allocator: Allocator) Allocator.Error![]usize {
            const allocator = self.allocator; // should maybe be separate (e.g. arena allocation while algo runs) but meh
            var level = try ArrayList(usize).initCapacity(allocator, 1);
            defer level.deinit(allocator);
            try level.appendSlice(allocator, states);
            var next_level = ArrayList(usize).initBuffer(&.{});
            defer next_level.deinit(allocator);
            var seen = try AutoHashMap(usize, void).init(allocator, states, allocator.alloc(void, states.len) catch unreachable);
            defer seen.deinit(allocator);
            while (level.items.len > 0) {
                for (level.items) |state| {
                    for (self.states.items[state].out.items) |transition| {
                        if (transition.char == null and seen.get(transition.to) == null) {
                            try seen.put(allocator, transition.to, {});
                            try next_level.append(allocator, transition.to);
                        }
                    }
                }
                level.clearRetainingCapacity();
                const old_level = level;
                level = next_level;
                next_level = old_level;
            }
            const res = try hull_allocator.alloc(usize, seen.count());
            var iter = seen.iterator();
            var i: usize = 0;
            while (iter.next()) |e| {
                res[i] = e.key_ptr.*;
                i += 1;
            }
            return res;
        }
        pub fn toDFA(self: Self, sub: Sub, merge_values: fn (v1: V, v2: V) V) Allocator.Error!DFA(V) {
            const allocator = self.allocator; // same here
            var dfa = DFA(V).init(allocator);
            var subsets = std.heap.ArenaAllocator.init(allocator);
            defer subsets.deinit();
            const start_subset = try self.hull(&.{sub.start}, subsets.allocator());
            var nfa_to_dfa = try HashMap(Subset, usize).init(allocator, &.{start_subset}, &.{
                try dfa.addState(contains(start_subset, sub.end), self.states.items[sub.start].value),
            });
            defer nfa_to_dfa.deinit(allocator);
            var next_level = ArrayList(Subset).initBuffer(&.{});
            defer next_level.deinit(allocator);
            var level = try ArrayList(Subset).initCapacity(allocator, 1);
            defer level.deinit(allocator);
            try level.append(allocator, start_subset);
            while (level.items.len > 0) {
                for (level.items) |subset| {
                    var transitions = try AutoHashMap(u8, AutoHashMap(usize, void)).init(allocator, &.{}, &.{});
                    defer {
                        for (transitions.values()) |*set|
                            set.deinit(allocator);
                        transitions.deinit(allocator);
                    }
                    for (subset) |state| {
                        for (self.states.items[state].out.items) |transition| {
                            if (transition.char) |char| {
                                if (transitions.getPtr(char)) |set| {
                                    try set.put(allocator, transition.to, {});
                                } else {
                                    try transitions.put(allocator, char, try AutoHashMap(usize, void).init(allocator, &.{transition.to}, &.{{}}));
                                }
                            }
                        }
                    }
                    var iter = transitions.iterator();
                    while (iter.next()) |e| {
                        var buf: [256]usize = undefined;
                        var i: usize = 0;
                        for (e.value_ptr.keys()) |state| {
                            buf[i] = state;
                            i += 1;
                        }
                        const reached_states = try self.hull(buf[0..i], subsets.allocator());
                        std.sort.pdq(usize, reached_states, {}, std.sort.asc(usize));
                        const gop = try nfa_to_dfa.getOrPut(allocator, reached_states);
                        if (!gop.found_existing) {
                            var v = self.states.items[reached_states[0]].value;
                            for (reached_states[1..]) |s|
                                v = merge_values(v, self.states.items[s].value);
                            const dfa_state = try dfa.addState(contains(reached_states, sub.end), v);
                            gop.value_ptr.* = dfa_state;
                            try next_level.append(allocator, reached_states);
                        }
                        try dfa.addTransition(nfa_to_dfa.get(subset).?, e.key_ptr.*, gop.value_ptr.*);
                    }
                }
                level.clearRetainingCapacity();
                const old_level = level;
                level = next_level;
                next_level = old_level;
            }
            return dfa;
        }
    };
}
test.zig 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
const std = @import("std");

const NFA = @import("nfa.zig").NFA(void);
const DFA = @import("dfa.zig").DFA(void);

const allocator = std.testing.allocator;

fn initNFA() NFA {
    return NFA.init(allocator, {});
}

fn nothing(_: void, _: void) void {
    return {};
}

const expect = std.testing.expect;

test "optional" {
    var nfa = initNFA();
    defer nfa.deinit();
    const sub = try nfa.opt(try nfa.str("test"));
    var dfa = try nfa.toDFA(sub, nothing);
    defer dfa.deinit();
    try expect(dfa.accepts("test"));
    try expect(dfa.accepts(""));
    try expect(!dfa.accepts("t"));
}

test "repetition" {
    var nfa = initNFA();
    defer nfa.deinit();
    const sub = try nfa.rep(try nfa.str("test"));
    var dfa = try nfa.toDFA(sub, nothing);
    defer dfa.deinit();
    try expect(dfa.accepts("test"));
    try expect(dfa.accepts("testtest"));
    try expect(!dfa.accepts(""));
    try expect(!dfa.accepts("t"));
    try expect(!dfa.accepts("testt"));
}

test "kleene" {
    var nfa = initNFA();
    defer nfa.deinit();
    const sub = try nfa.kleene(try nfa.str("test"));
    var dfa = try nfa.toDFA(sub, nothing);
    defer dfa.deinit();
    try expect(dfa.accepts("testtesttest"));
    try expect(dfa.accepts("test"));
    try expect(dfa.accepts(""));
    try expect(!dfa.accepts("t"));
    try expect(!dfa.accepts("testyay"));
}

test "concat" {
    var nfa = initNFA();
    defer nfa.deinit();
    const sub = try nfa.concat(&.{ try nfa.str("hello"), try nfa.str("world") });
    var dfa = try nfa.toDFA(sub, nothing);
    defer dfa.deinit();
    try expect(dfa.accepts("helloworld"));
    try expect(!dfa.accepts(""));
    try expect(!dfa.accepts("hello"));
    try expect(!dfa.accepts("world"));
}

test "choice" {
    var nfa = initNFA();
    defer nfa.deinit();
    const sub = try nfa.choice(&.{ try nfa.str("hi"), try nfa.str("hello") });
    var dfa = try nfa.toDFA(sub, nothing);
    defer dfa.deinit();
    try expect(dfa.accepts("hi"));
    try expect(dfa.accepts("hello"));
    try expect(!dfa.accepts(""));
    try expect(!dfa.accepts("h"));
    try expect(!dfa.accepts("helloworld"));
}

test "putting it all together" {
    var nfa = initNFA();
    defer nfa.deinit();
    const sub = try nfa.choice(&.{
        try nfa.concat(&.{
            try nfa.rep(try nfa.str("ab")),
            try nfa.str("cd"),
        }),
        try nfa.kleene(try nfa.str("ef")),
    });
    var dfa = try nfa.toDFA(sub, nothing);
    defer dfa.deinit();
    try expect(dfa.accepts("abcd"));
    try expect(dfa.accepts("abababcd"));
    try expect(dfa.accepts("efef"));
    try expect(dfa.accepts(""));
    try expect(!dfa.accepts("abcdef"));
    try expect(!dfa.accepts("g"));
    try expect(!dfa.accepts("a"));
}

const origRegexToDFA = @import("ex.zig").regexToDFA;
fn regexToDFA(regex: []const u8) !DFA {
    return origRegexToDFA(allocator, regex);
}

test "simple regex" {
    var dfa = try regexToDFA("(ab)+cd|(ef)*");
    defer dfa.deinit();
    try expect(dfa.accepts("abcd"));
    try expect(dfa.accepts("abababcd"));
    try expect(dfa.accepts("efef"));
    try expect(dfa.accepts(""));
    try expect(!dfa.accepts("abcdef"));
    try expect(!dfa.accepts("g"));
    try expect(!dfa.accepts("a"));
}

test "charset" {
    var dfa = try regexToDFA("[a%]|b]");
    defer dfa.deinit();
    try expect(dfa.accepts("a"));
    try expect(dfa.accepts("b"));
    try expect(dfa.accepts("]"));
    try expect(dfa.accepts("|"));
    try expect(!dfa.accepts("c"));
}

test "charset range" {
    var dfa = try regexToDFA("[a-c]");
    defer dfa.deinit();
    try expect(dfa.accepts("a"));
    try expect(dfa.accepts("b"));
    try expect(!dfa.accepts("d"));
}

test "charset complement" {
    var dfa = try regexToDFA("[^ab]");
    defer dfa.deinit();
    try expect(!dfa.accepts("a"));
    try expect(!dfa.accepts("b"));
    try expect(dfa.accepts("c"));
}
ansi.zig 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
// The necessary parts of ANSI escape sequence support required for this editor to work

const std = @import("std");
const fmt = std.fmt;
const Color = @import("color.zig").Color;
const Pos = @import("pos.zig");

pub fn parseCsiParams(res: []usize, comptime default: usize, params: []const u8, min_params: usize) ![]usize {
    var i: usize = 0;
    var si = std.mem.splitScalar(u8, params, ';');
    while (i < res.len) : (i += 1) {
        const param = si.next();
        if (param) |num| {
            res[i] = if (num.len == 0) default else try std.fmt.parseInt(usize, num, 10);
        } else {
            res[i] = default;
            break;
        }
    }
    if (i == res.len and si.next() != null)
        return error.TooManyParameters;
    while (i < min_params) : (i += 1)
        res[i] = default;
    return res[0..i];
}

pub const AnsiCommand = union(enum) {
    ignore, // something we don't recognize
    tab,
    backspace,
    delete,
    newline,
    carriage_return,
    insert,
    // Control characters (excluding those corresponding to the above)
    ctrl: u8, // a - z
    // Regular printable ASCII key presses
    print: u8,
    cursor_movement: CursorMovement,
    const CursorMovement = struct {
        const Action = enum { left, right, up, down, pos1, end };
        modifier: enum { none, shift, ctrl },
        action: Action,
        times: usize,
    };
};

pub const AnsiCommandIterator = struct {
    buf: []const u8,
    i: usize,
    fn getch(self: *AnsiCommandIterator) ?u8 {
        if (self.eof()) return null;
        const c = self.buf[self.i];
        self.i += 1;
        return c;
    }
    fn ungetch(self: *AnsiCommandIterator) void {
        self.i -= 1;
    }
    fn eof(self: *AnsiCommandIterator) bool {
        return self.i >= self.buf.len;
    }
    fn parseCursorMovement(
        kind: u8,
        params: []const u8,
    ) AnsiCommand {
        var movbuf: [2]usize = undefined;
        const movement = parseCsiParams(&movbuf, 1, params, 1) catch return .{ .ignore = {} };
        const action: AnsiCommand.CursorMovement.Action = switch (kind) {
            'A' => .up,
            'B' => .down,
            'C' => .right,
            'D' => .left,
            'H' => .pos1,
            'F' => .end,
            else => return .{ .ignore = {} },
        };
        const times = movement[0];
        return if (movement.len == 1)
            .{ .cursor_movement = .{ .modifier = .none, .action = action, .times = times } }
        else if (movement.len == 2)
            switch (movement[1]) {
                2 => .{ .cursor_movement = .{ .modifier = .shift, .action = action, .times = times } },
                5 => .{ .cursor_movement = .{ .modifier = .ctrl, .action = action, .times = times } },
                else => .{ .ignore = {} },
            }
        else
            .{ .ignore = {} };
    }
    pub fn next(self: *AnsiCommandIterator) ?AnsiCommand {
        const c = self.getch() orelse return null;
        return switch (c) {
            else => .{ .ignore = {} },
            0x08 => .{ .backspace = {} },
            0x7F => .{ .delete = {} },
            '\n' => .{ .newline = {} },
            '\r' => .{ .carriage_return = {} },
            '\t' => .{ .tab = {} },
            0x01...0x07, 0x0C, 0x0E...0x1A => .{ .ctrl = c - 1 + 'a' },
            // Printable ASCII
            ' '...'~' => .{ .print = c },
            // ANSI escape sequences
            0x1B => switch (self.getch() orelse return .{ .ignore = {} }) { // TODO possible ESC
                else => .{ .ignore = {} },
                // Fe sequence
                0x40...0x5A, 0x5C...0x5F => .{ .ignore = {} },
                '[' => {
                    // CSI sequence
                    if (self.eof()) return .{ .ignore = {} }; // TODO cut off?
                    const start = self.i;
                    var c2 = self.getch() orelse return .{ .ignore = {} };
                    while (c2 >= 0x30 and c2 <= 0x3F)
                        c2 = self.getch() orelse return .{ .ignore = {} };
                    while (c2 >= 0x20 and c2 <= 0x2F)
                        c2 = self.getch() orelse return .{ .ignore = {} };
                    if (c2 < 0x40 or c2 > 0x7E) {
                        self.ungetch();
                        return .{ .ignore = {} }; // TODO syntax error
                    }
                    const params = self.buf[start .. self.i - 1];
                    if (c2 == '~') return switch (fmt.parseInt(usize, params, 10) catch return .{ .ignore = {} }) {
                        2 => .{ .insert = {} },
                        else => .{ .ignore = {} },
                    };
                    return parseCursorMovement(c2, params);
                },
            },
        };
    }
};

pub fn parse(buf: []const u8) AnsiCommandIterator {
    return .{ .buf = buf, .i = 0 };
}

pub fn writeCsi(writer: anytype, seq: []const u8) !void {
    try writer.writeAll("\x1B[");
    try writer.writeAll(seq);
}

pub fn fmtCsi(writer: anytype, csi_type: u8, params: anytype) !void {
    try writer.writeAll("\x1B[");
    if (params.len > 0)
        try fmt.format(writer, "{d}", .{params[0]});
    var i: usize = 1;
    while (i < params.len) : (i += 1)
        try fmt.format(writer, ";{d}", .{params[i]});
    try writer.writeByte(csi_type);
}

pub fn setCursor(writer: anytype, pos: Pos) !void {
    return fmtCsi(writer, 'H', [_]usize{ pos.row + 1, pos.col + 1 });
}

pub fn setFg(writer: anytype, color: Color) !void {
    return fmtCsi(writer, 'm', [_]usize{ 38, 2, color.r, color.g, color.b });
}

pub fn setBg(writer: anytype, color: Color) !void {
    return fmtCsi(writer, 'm', [_]usize{ 48, 2, color.r, color.g, color.b });
}

pub fn clearScreen(writer: anytype) !void {
    return writeCsi(writer, "2J");
}
c.zig 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
const hex_digit = "[0-9A-Fa-f]";
const integer = "(([1-9][0-9]*|0[0-7]*|0[xX]" ++ hex_digit ++ "+)([uU](l|L|ll|LL)|(l|L|ll|LL)[uU]))";
const dexp = "([eE][+-]?[0-9]+)";
const hexp = "([pP][+-]?[0-9]+)";
const decfloat = "([0-9]+%.[0-9]*|%.[0-9]+)" ++ dexp ++ "?|[0-9]+" ++ dexp;
const hexfloat = "0[xX](" ++ hex_digit ++ "+%." ++ hex_digit ++ "*" ++ hexp ++ "?|" ++ hex_digit ++ "+" ++ hexp ++ ")";
const float = "(" ++ decfloat ++ "|" ++ hexfloat ++ ")[fF]";
// TODO fallback tainted invalid escape sequence
const hex_quad = hex_digit ++ hex_digit ++ hex_digit ++ hex_digit;
const escape = "(\\(['\"?\\abfnrtv]|[0-7][0-7]?[0-7]?|x" ++ hex_digit ++ "+|u" ++ hex_quad ++ "|U" ++ hex_quad ++ hex_quad ++ "))";

const tok = @import("tok.zig");
const RegexTokenizer = tok.RegexTokenizer;
const Token = tok.Token;

var cache: ?RegexTokenizer = null;
pub fn tokenizer() !RegexTokenizer {
    cache = cache orelse try RegexTokenizer.init(&.{
        // zig fmt: off
        .{.keyword, "auto|break|case|char|const|continue|default|do|double"
            ++ "|else|enum|extern|float|for|goto|if|inline|int|long"
            ++ "|register|restrict|return|short|signed|sizeof|static|struct|switch"
            ++ "|typedef|union|unsigned|void|volatile|while"
            ++ "|_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local"},
        .{.identifier, "[a-zA-Z_]+[a-zA-Z0-9_]*"},
        .{.quoted, "(u8|[uUL])?\"([^\"\n\\]|" ++ escape ++ ")*\"|[uUL]?'([^'\n\\]|" ++ escape ++ ")*'"},
        .{.number, integer ++ "|" ++ float},
        .{.sym, "[(){}[%]<>.!&|+%-*/%]|%->|%+%+|%-%-|<<|>>|[!=<>]=|%^|&&|%|%||%?|:|;|%.%.%.|([*/&+%-^|]|<<|>>)?=|,|#|##"
            ++ "|<:|:>|<%%|%%>|%%:|%%:%%:"}, // darn digraphs
        .{.pp, "#include[ \t]*\"[^\n\"]*\"|<[^\n>]*>|#(pragma|define|undef|if(n?def)|else|elif|endif|line|error|warning)"},
        .{.comment, "//[^\n]*|/%*(/[^*]|[^/])*%*/"},
        .{.ignore, "[ \n\r\t]+"},
    });
    return cache.?;
}

const expectEq = @import("std").testing.expectEqual;
const Tester = struct {
    t: RegexTokenizer,
    buf: []const u8,
    i: usize = 0,
    pub fn expect(self: *@This(), kind: tok.TokenType, s: []const u8) !void {
        try expectEq(Token{.kind = kind, .len = s.len}, self.t.nextToken(self.buf[self.i..]));
        self.i += s.len;
    }
};
test "things" {
    const buf = "if (1) printf(\"blah\");";
    var t = Tester{.t = try tokenizer(), .buf = buf};
    try t.expect(.keyword, "if");
    try t.expect(.ignore, " ");
    try t.expect(.sym, "(");
    try t.expect(.number, "2");
    try t.expect(.sym, ")");
    try t.expect(.ignore, " ");
    try t.expect(.identifier, "printf");
    try t.expect(.sym, "(");
    try t.expect(.quoted, "\"blah\"");
    try t.expect(.sym, ")");
    try t.expect(.sym, ";");
}
color.zig ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
pub const Color = struct { r: u8, g: u8, b: u8 };

fn h(u: usize) Color {
    return .{ .r = (u >> 16) & 0xFF, .g = (u >> 8) & 0xFF, .b = u & 0xFF };
}

// not doing proper enum array rn
pub const FG = [_]Color{
    h(0x66ff66), // keyword
    h(0x116611), // identifier
    h(0xff7611), // quoted
    h(0x6666ff), // number
    h(0x113311), // sym
    h(0x1d0878), // pp
    h(0x888888), // comment
    h(0x111111), // ignore
};

pub const BG = Color{ .r = 255, .g = 255, .b = 255 };
editor.zig 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
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
const std = @import("std");
const os = std.os;
const posix = std.posix;
const File = std.fs.File;
const ArrayList = std.ArrayList;

const ansi = @import("ansi.zig");
const AnsiCommand = ansi.AnsiCommand;
const txtbuf = @import("txtbuf.zig");
const color = @import("color.zig");
const util = @import("util.zig");

const Pos = @import("pos.zig");
const TextBuffer = txtbuf.TextBuffer;

const tok = @import("tok.zig");

fn asgn(u: usize) isize {
    return @intCast(u);
}

fn asun(i: isize) usize {
    return @intCast(i);
}

const Allocator = std.mem.Allocator;

const Edit = struct {
    const Self = @This();
    at: usize,
    old: []const u8,
    new: []const u8,
    fn init(at: usize, old: []const u8, new: []const u8, a: Allocator) !Self {
        const self_new = try a.alloc(u8, new.len);
        @memcpy(self_new, new);
        const self_old = try a.alloc(u8, old.len);
        @memcpy(self_old, old);
        return .{ .at = at, .new = self_new, .old = self_old };
    }
    // Note: To play nice with arena allocators, this must be in reverse order vs. init!
    fn deinit(self: Self, a: Allocator) void {
        a.free(self.old);
        a.free(self.new);
    }
    fn redo(self: Self, buf: *TextBuffer) !usize {
        try buf.replaceRange(self.at, self.old.len, self.new);
        return self.at + self.new.len;
    }
    fn undo(self: Self, buf: *TextBuffer) !usize {
        try buf.replaceRange(self.at, self.new.len, self.old);
        return self.at + self.old.len;
    }
};

const allocator = std.heap.c_allocator;

// TODO cap history size

const History = struct {
    const Self = @This();
    const ArenaAllocator = std.heap.ArenaAllocator;
    arena: ArenaAllocator,
    edits: ArrayList(Edit),
    cursor: usize,
    pub fn init() Self {
        const arena = ArenaAllocator.init(std.heap.page_allocator);
        return .{
            .arena = arena,
            .edits = ArrayList(Edit).init(allocator),
            .cursor = 0,
        };
    }
    pub fn deinit(self: Self) void {
        self.arena.deinit();
        self.edits.deinit();
    }

    fn undo(self: *Self, text: *TextBuffer) !?usize {
        if (self.cursor == 0)
            return null;
        self.cursor -= 1;
        return try self.edits.items[self.cursor].undo(text);
    }

    fn redo(self: *Self, text: *TextBuffer) !?usize {
        if (self.cursor == self.edits.items.len)
            return null;
        const res = try self.edits.items[self.cursor].redo(text);
        self.cursor += 1;
        return res;
    }

    fn record(self: *Self, at: usize, old: []const u8, new: []const u8) !void {
        // TODO do better than throwing edits away
        // Deinitialize *in reverse order* so the arena can actually free the memory
        if (self.cursor < self.edits.items.len) {
            var i: usize = self.edits.items.len - 1;
            while (true) {
                self.edits.items[i].deinit(self.arena.allocator());
                if (i == self.cursor) break;
                i -= 1;
            }
            self.edits.shrinkRetainingCapacity(self.cursor);
        }
        try self.edits.append(try Edit.init(at, old, new, self.arena.allocator()));
        self.cursor += 1;
    }
};

const Clipboard = struct {
    const Self = @This();
    content: []u8,
    pub fn init() !Self {
        return .{ .content = try allocator.alloc(u8, 0) };
    }
    pub fn deinit(self: Self) void {
        allocator.free(self.content);
    }
    pub fn get(self: Self) []const u8 {
        return self.content;
    }
    pub fn set(self: *Self, content: []const u8) !void {
        self.content = try allocator.realloc(self.content, content.len);
        @memcpy(self.content, content);
    }
};

const TextPane = struct {
    const Self = @This();
    anchor: Pos,
    cursor: Pos,
    mark: ?Pos,
    text: TextBuffer,
    history: History,
    clipboard: *Clipboard,
    multiline: bool,
    pub fn init(clipboard: *Clipboard, multiline: bool) !Self {
        return .{
            .anchor = .{ .row = 0, .col = 0 },
            .cursor = .{ .row = 0, .col = 0 },
            .mark = null,
            .text = try TextBuffer.empty(),
            .history = History.init(),
            .clipboard = clipboard,
            .multiline = multiline,
        };
    }
    pub fn deinit(self: *Self) void {
        self.text.deinit();
        self.history.deinit();
    }

    fn setCursor(self: *Self, cursor: Pos) void {
        self.cursor = cursor;
        self.mark = null;
    }

    pub fn setText(self: *Self, text: TextBuffer) void {
        self.text = text;
        self.setCursor(.{ .row = 0, .col = 0 });
        self.anchor = .{ .row = 0, .col = 0 };
        self.history.deinit();
        self.history = History.init();
    }

    fn moveRows(self: *Self, d: isize) Pos {
        const row = @max(0, @min(asgn(self.text.nRows()) - 1, asgn(self.cursor.row) + d));
        const col = @max(0, @min(self.text.nCols(row), self.cursor.col));
        return .{ .row = row, .col = col };
    }

    fn moveCols(self: *Self, d_cols: isize) Pos {
        var cursor = self.cursor;
        var d = d_cols;
        while (d < 0) {
            const col = asgn(cursor.col) + d;
            if (col >= 0) {
                cursor.col = asun(col);
                break;
            }
            if (cursor.row == 0) {
                cursor.col = 0;
                break;
            }
            cursor.row -= 1;
            cursor.col = self.text.nCols(cursor.row);
            d = col + 1;
        }
        while (d > 0) {
            const col = asgn(cursor.col) + d;
            const nCols = self.text.nCols(cursor.row);
            if (col <= nCols) {
                cursor.col = asun(col);
                break;
            }
            if (cursor.row == self.text.nRows() - 1) {
                cursor.col = nCols;
                break;
            }
            cursor.row += 1;
            cursor.col = 0;
            d = col - asgn(nCols) - 1;
        }
        return cursor;
    }

    fn getCursorIdx(self: Self) usize {
        return self.text.getIdx(self.cursor);
    }

    const Span = struct { min: usize, max: usize };
    fn getSelection(self: Self) ?Span {
        if (self.mark) |mark| {
            const idx = self.getCursorIdx();
            const mark_idx = self.text.getIdx(mark);
            return .{ .min = @min(idx, mark_idx), .max = @max(idx, mark_idx) };
        }
        return null;
    }

    fn getCopySpan(self: Self) Span {
        const idx = self.text.getIdx(.{ .row = self.cursor.row, .col = 0 });
        return self.getSelection() orelse Span{ .min = idx, .max = idx + self.text.nCols(self.cursor.row) + 1 };
    }

    fn copy(self: *Self) !void {
        const sel = self.getCopySpan();
        try self.clipboard.set(self.text.getRange(sel.min, sel.max));
    }

    fn deleteSpan(self: *Self, sel: Span) !void {
        const old = self.text.getRange(sel.min, sel.max);
        try self.history.record(sel.min, old, &.{});
        try self.text.replaceRange(sel.min, old.len, &.{});
        self.setCursor(self.text.getPos(sel.min));
    }

    fn delete(self: *Self) !void {
        const idx = self.getCursorIdx();
        const sel = self.getSelection() orelse Span{ .min = if (idx == 0) return else idx - 1, .max = idx };
        return self.deleteSpan(sel);
    }

    fn cut(self: *Self) !void {
        try self.copy();
        try self.deleteSpan(self.getCopySpan());
    }

    fn insert(self: *Self, new: []const u8) !void {
        if (!self.multiline and std.mem.indexOfScalar(u8, new, '\n') != null)
            return;
        const cur_idx = self.getCursorIdx();
        const sel = self.getSelection() orelse Span{ .min = cur_idx, .max = cur_idx };
        const old = self.text.getRange(sel.min, sel.max);
        try self.history.record(sel.min, old, new);
        try self.text.replaceRange(sel.min, old.len, new);
        self.setCursor(self.text.getPos(sel.min + new.len));
    }

    fn insertNewline(self: *Self) !void {
        const start_row = if (self.mark) |pos| @min(pos.row, self.cursor.row) else self.cursor.row;
        const start_col = if (start_row < self.cursor.row) self.mark.?.col else @min((self.mark orelse self.cursor).col, self.cursor.col);
        const start_idx = self.text.getIdx(.{ .row = start_row, .col = 0 });
        const line = self.text.getRange(start_idx, start_idx + self.text.nCols(start_row));
        var i: usize = 0;
        while (i < start_col and (line[i] == ' ' or line[i] == '\t')) i += 1;
        const indent = line[0..i];
        const indented_line = try std.mem.concat(allocator, u8, &.{ "\n", indent });
        defer allocator.free(indented_line);
        try self.insert(indented_line);
    }

    fn paste(self: *Self) !void {
        try self.insert(self.clipboard.get());
    }

    fn undo(self: *Self) !void {
        if (try self.history.undo(&self.text)) |idx|
            self.setCursor(self.text.getPos(idx));
    }

    fn redo(self: *Self) !void {
        if (try self.history.redo(&self.text)) |idx|
            self.setCursor(self.text.getPos(idx));
    }

    fn selectAll(self: *Self) void {
        self.cursor = .{ .row = 0, .col = 0 };
        self.mark = .{ .row = self.text.nRows() - 1, .col = self.text.nCols(self.text.nRows() - 1) };
    }

    fn selectToken(self: *Self) void {
        var hint = tok.Tokenization.Hint{};
        const token = self.text.tokens.getToken(self.getCursorIdx(), &hint);
        // HACK don't let the trailing newline disappear
        self.cursor = self.text.getPos(@min(hint.sum, self.text.chars.items.len - 1));
        self.mark = self.text.getPos(hint.sum - token.len);
    }

    fn gotoPrevToken(self: *Self) void {
        self.setCursor(self.text.getPos(self.text.tokens.getPreviousTokenStart(self.getCursorIdx())));
    }

    fn gotoNextToken(self: *Self) void {
        self.setCursor(self.text.getPos(self.text.tokens.getNextTokenEnd(self.getCursorIdx())));
    }

    pub fn processCommand(self: *Self, cmd: AnsiCommand) !void {
        switch (cmd) {
            else => {},
            .backspace, .delete => try self.delete(),
            .newline, .carriage_return => try self.insertNewline(),
            .print => |c| try self.insert(&[_]u8{c}),
            .tab => try self.insert("    "), // HACK awful
            .ctrl => |c| switch (c) {
                'z' => try self.undo(),
                'y' => try self.redo(),
                'c' => try self.copy(),
                'x' => try self.cut(),
                'v' => try self.paste(),
                'a' => self.selectAll(),
                't' => self.selectToken(),
                else => {},
            },
            .cursor_movement => |cm| switch (cm.modifier) {
                .none => switch (cm.action) {
                    .up => self.setCursor(self.moveRows(-asgn(cm.times))),
                    .down => self.setCursor(self.moveRows(asgn(cm.times))),
                    .right => self.setCursor(self.moveCols(asgn(cm.times))),
                    .left => self.setCursor(self.moveCols(-asgn(cm.times))),
                    .pos1 => self.setCursor(.{ .col = 0, .row = self.cursor.row }),
                    .end => self.setCursor(.{ .col = self.text.nCols(self.cursor.row), .row = self.cursor.row }),
                },
                .ctrl => switch (cm.action) {
                    .up, .down => {}, // TODO
                    .left => self.gotoPrevToken(),
                    .right => self.gotoNextToken(),
                    .pos1 => self.setCursor(.{ .row = 0, .col = 0 }),
                    .end => self.setCursor(.{ .row = self.text.nRows() - 1, .col = self.text.nCols(self.text.nRows() - 1) }),
                },
                .shift => {
                    if (self.mark == null)
                        self.mark = self.cursor;
                    switch (cm.action) {
                        .up => self.cursor = self.moveRows(-asgn(cm.times)),
                        .down => self.cursor = self.moveRows(asgn(cm.times)),
                        .right => self.cursor = self.moveCols(asgn(cm.times)),
                        .left => self.cursor = self.moveCols(-asgn(cm.times)),
                        .pos1 => self.cursor.col = 0,
                        .end => self.cursor.col = self.text.nCols(self.cursor.row),
                    }
                },
            },
        }
    }

    fn scroll(self: *Self, dim: Pos) void {
        self.anchor = self.anchor.min(self.cursor);
        const bottom_right = self.anchor.add(dim);
        if (self.cursor.row >= bottom_right.row)
            self.anchor.row += self.cursor.row - bottom_right.row + 1;
        if (self.cursor.col >= bottom_right.col)
            self.anchor.col += self.cursor.col - bottom_right.col + 1;
    }

    const ColorManager = struct {
        const CM = @This();
        fg: color.Color,
        bg: color.Color,
        pub fn init(writer: anytype) !CM {
            const fg = color.FG[@intFromEnum(tok.TokenType.ignore)];
            try ansi.setFg(writer, fg);
            const bg = color.BG;
            try ansi.setBg(writer, bg);
            return .{
                .fg = fg,
                .bg = bg,
            };
        }
        pub fn set(self: *CM, writer: anytype, t: tok.TokenType, sel: bool) !void {
            var fg = color.FG[@intFromEnum(t)];
            var bg = color.BG;
            if (sel)
                std.mem.swap(color.Color, &fg, &bg);
            if (!std.meta.eql(fg, self.fg)) {
                try ansi.setFg(writer, fg);
                self.fg = fg;
            }
            if (!std.meta.eql(bg, self.bg)) {
                try ansi.setBg(writer, bg);
                self.bg = bg;
            }
        }
    };

    fn render(self: *Self, dim: Pos, writer: anytype, focus: bool) !void {
        self.scroll(dim);

        const idx = self.getCursorIdx();
        const sel = self.getSelection() orelse Span{ .min = idx, .max = idx + 1 };

        var row: usize = 0;
        var cm = try ColorManager.init(writer);
        var tok_hint: tok.Tokenization.Hint = .{};
        while (row < dim.row) : (row += 1) {
            var col: usize = 0;
            while (col < dim.col) : (col += 1) {
                const pos = .{ .row = row, .col = col };
                const abs = self.anchor.add(pos);
                const c = self.text.get(abs);
                var highlight = false;
                var tt = tok.TokenType.ignore;
                if (abs.row < self.text.nRows() and abs.col <= self.text.nCols(abs.row)) {
                    const c_idx = self.text.getIdx(abs);
                    if (focus) highlight = c_idx >= sel.min and c_idx < sel.max;
                    tt = self.text.tokens.getToken(c_idx, &tok_hint).kind;
                }
                try cm.set(writer, tt, highlight);
                switch (c orelse ' ') {
                    0x20...0x7e => |ch| try writer.writeByte(ch),
                    else => try writer.writeAll(" "),
                }
            }
            if (row + 1 < dim.row)
                try writer.writeAll("\r\n");
        }
        try cm.set(writer, .ignore, false);
    }
};

pub const Editor = struct {
    const Self = @This();
    const BW = @TypeOf(std.io.bufferedWriter(std.io.getStdOut().writer()));
    tty: File,
    tui: *BW,
    path: []u8,
    clipboard: *Clipboard,
    textpane: TextPane,
    state: union(enum) {
        normal: void,
        confirm_quit: void,
        save_path: TextPane,
    },
    pub fn init(tty: File, tui: *BW) !Self {
        const clipboard = try allocator.create(Clipboard);
        clipboard.* = try Clipboard.init();
        return .{
            .tty = tty,
            .tui = tui,
            .path = try allocator.alloc(u8, 0),
            .clipboard = clipboard,
            .textpane = try TextPane.init(clipboard, true),
            .state = .normal,
        };
    }
    pub fn deinit(self: *Self) void {
        self.textpane.deinit();
        self.clipboard.deinit();
        allocator.free(self.path);
        allocator.destroy(self.clipboard);
    }

    fn setPath(self: *Self, path: []const u8) !void {
        self.path = try allocator.realloc(self.path, path.len);
        @memcpy(self.path, path);
    }

    pub fn open(self: *Self, path: []const u8) !void {
        if (TextBuffer.open(path)) |text| {
            self.textpane.setText(text);
            try self.setPath(path);
        } else |err| switch (err) {
            error.FileNotFound => {
                self.textpane.setText(try TextBuffer.empty());
                try self.setPath(path);
            },
            else => try self.setPath(""),
        }
    }

    fn isSaved(self: *Self) bool {
        if (self.path.len == 0)
            return self.textpane.text.chars.items.len == 0;
        const file = std.fs.cwd().openFile(self.path, .{ .mode = .read_only }) catch return false;
        var chars = ArrayList(u8).init(allocator);
        defer chars.deinit();
        file.reader().readAllArrayList(&chars, 16384) catch return false;
        return std.mem.eql(u8, self.textpane.text.chars.items, chars.items);
    }

    fn save(self: *Self) !void {
        if (self.path.len > 0) {
            try self.textpane.text.save(self.path); // TODO error handling
        } else {
            self.state = .{ .save_path = try TextPane.init(self.clipboard, false) };
        }
    }

    fn processCommand(self: *Self, cmd: AnsiCommand) !bool {
        switch (self.state) {
            .normal => switch (cmd) {
                .ctrl => |c| switch (c) {
                    'q' => {
                        if (self.isSaved())
                            return true;
                        self.state = .{ .confirm_quit = {} };
                    },
                    's' => try self.save(),
                    else => try self.textpane.processCommand(cmd),
                },
                else => try self.textpane.processCommand(cmd),
            },
            .confirm_quit => switch (cmd) {
                .ctrl => |c| switch (c) {
                    'q' => return true,
                    else => self.state = .{ .normal = {} },
                },
                else => self.state = .{ .normal = {} },
            },
            .save_path => |prompt| switch (cmd) {
                .ctrl => |c| switch (c) {
                    'q' => {
                        self.state.save_path.deinit();
                        self.state = .{ .normal = {} };
                    },
                    else => try self.state.save_path.processCommand(cmd),
                },
                .newline, .carriage_return => {
                    const path = prompt.text.chars.items;
                    try self.setPath(path[0 .. path.len - 1]); // trim trailing newline (TODO refucktor)
                    try self.textpane.text.save(self.path); // TODO error handling
                    self.state.save_path.deinit();
                    self.state = .{ .normal = {} };
                },
                else => try self.state.save_path.processCommand(cmd),
            },
        }
        return false;
    }

    fn processInput(self: *Self, buf: []const u8) !bool {
        var iter = ansi.parse(buf);
        while (iter.next()) |cmd| {
            switch (cmd) {
                .ignore => {},
                .insert => @import("e/josh.zig").josh(self.tty, self.tui) catch {},
                else => if (try self.processCommand(cmd)) return true,
            }
        }
        return false;
    }

    fn render(self: *Self) !void {
        const us = std.time.microTimestamp();
        const writer = self.tui.writer();
        const dim = try util.getTtySize(self.tty);
        try ansi.setCursor(writer, .{ .row = 0, .col = 0 });
        try self.textpane.render(.{ .row = dim.row - 1, .col = dim.col }, writer, self.state == .normal);
        try writer.writeAll("\r\n");

        switch (self.state) {
            .normal => {
                var buf: [1000]u8 = undefined;
                var fixbuf = std.io.fixedBufferStream(&buf);
                @memset(buf[0..dim.col], ' ');
                std.fmt.format(fixbuf.writer(), "{s}:{d}:{d}  {d} us  {d} lines", .{
                    if (self.path.len == 0) "?" else self.path,
                    self.textpane.cursor.row + 1,
                    self.textpane.cursor.col + 1,
                    std.time.microTimestamp() - us,
                    self.textpane.text.nRows(),
                }) catch {};
                try writer.writeAll(buf[0..dim.col]);
            },
            .confirm_quit => {
                const msg = "You have unsaved changes. Press C-q to quit, any other key to cancel.";
                try writer.writeAll(if (dim.col < msg.len) msg[0..dim.col] else msg);
                var i = msg.len;
                while (i < dim.col) : (i += 1) try writer.writeByte(' ');
            },
            .save_path => {
                const prefix = "save as: ";
                try writer.writeAll(prefix);
                try self.state.save_path.render(.{ .row = 1, .col = dim.col - prefix.len }, writer, true);
            },
        }

        try self.tui.flush();
    }

    pub fn run(self: *Self) !void {
        try self.render();
        var buf: [1000]u8 = undefined;
        while (true) {
            // TODO deal with full buffer (unlikely in normal usage)
            const len = try self.tty.read(&buf);
            if (try self.processInput(buf[0..len]))
                return;
            try self.render();
        }
    }
};
main.zig 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
const std = @import("std");
const fs = std.fs;
const os = std.os;
const posix = std.posix;

const ansi = @import("ansi.zig");
const Editor = @import("editor.zig").Editor;

fn uncook(handle: anytype, tcattr: anytype) !void {
    var raw_attr = tcattr;
    raw_attr.lflag.ECHO = false;
    raw_attr.lflag.ICANON = false;
    raw_attr.lflag.ISIG = false;
    raw_attr.lflag.IEXTEN = false;
    raw_attr.iflag.IXON = false;
    raw_attr.iflag.ICRNL = false;
    raw_attr.iflag.BRKINT = false;
    raw_attr.iflag.INPCK = false;
    raw_attr.iflag.ISTRIP = false;
    raw_attr.oflag.OPOST = false;
    raw_attr.cflag.CSIZE = .CS8;
    // TODO think about these
    raw_attr.cc[@intFromEnum(std.posix.V.TIME)] = 0;
    raw_attr.cc[@intFromEnum(std.posix.V.MIN)] = 1;
    try std.posix.tcsetattr(handle, .FLUSH, raw_attr);
}

fn run() !void {
    var tty = try fs.cwd().openFile("/dev/tty", .{ .mode = .read_write });
    defer tty.close();

    const cooked_attr = try std.posix.tcgetattr(tty.handle);
    defer std.posix.tcsetattr(tty.handle, .FLUSH, cooked_attr) catch {};
    try uncook(tty.handle, cooked_attr);

    const stdout_file = std.io.getStdOut().writer();
    var bw = std.io.bufferedWriter(stdout_file);
    defer bw.flush() catch {};
    const stdout = bw.writer();
    const writeCsi = ansi.writeCsi;

    // Hide / unhide cursor
    try writeCsi(stdout, "?25l");
    defer writeCsi(stdout, "?25h") catch {};
    // Mouse on / off
    // try writeCsi(stdout, "?1000h");
    // defer writeCsi(stdout, "?1000l") catch {};
    // Save / restore cursor pos
    try writeCsi(stdout, "s");
    defer writeCsi(stdout, "u") catch {};
    // Save / restore screen
    try writeCsi(stdout, "?47h");
    defer writeCsi(stdout, "?47l") catch {};
    // Enable alternative buffer / switch back to original buffer
    try writeCsi(stdout, "?1049h");
    defer writeCsi(stdout, "?1049l") catch {};
    try bw.flush();

    var editor = try Editor.init(tty, &bw);
    defer editor.deinit();
    var args = std.process.args();
    if (args.next()) |_| {
        if (args.next()) |path| {
            if (args.next() == null)
                try editor.open(path);
        }
    }
    try editor.run();
}

pub fn main() !void {
    return run() catch |e| switch (e) {
        error.OutOfMemory => std.io.getStdOut().writer().writeAll(@import("e/oom.zig").oom),
        else => e,
    };
}
plain.zig ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const tok = @import("tok.zig");
const RegexTokenizer = tok.RegexTokenizer;
const Token = tok.Token;

var cache: ?RegexTokenizer = null;
pub fn tokenizer() !RegexTokenizer {
    cache = cache orelse try RegexTokenizer.init(&.{
        // zig fmt: off
        .{.identifier, "[^ \n\r\t]+"},
        .{.ignore, "[ \n\r\t]+"},
    });
    return cache.?;
}
pos.zig ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const Pos = @This();
row: usize,
col: usize,
pub fn add(p: Pos, q: Pos) Pos {
    return Pos{ .row = p.row + q.row, .col = p.col + q.col };
}
pub fn sub(p: Pos, q: Pos) Pos {
    return Pos{ .row = p.row - q.row, .col = p.col - q.col };
}
pub fn min(p: Pos, q: Pos) Pos {
    return Pos{ .row = @min(p.row, q.row), .col = @min(p.col, q.col) };
}
tok.zig 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
const std = @import("std");

const NFA = @import("reg/nfa.zig").NFA(StateInfo);
const DFA = @import("reg/dfa.zig").DFA(StateInfo);
const RegexReader = @import("reg/ex.zig").RegexReader(StateInfo);
const allocator = std.heap.c_allocator;

pub const TokenType = enum(u8) {
    keyword,
    identifier,
    quoted,
    number,
    sym,
    pp,
    comment,
    ignore,
};

pub const Token = struct {
    kind: TokenType,
    len: usize,
};

const StateInfo = struct {
    tt: TokenType,
    acc: bool,
};

fn mergeTokens(a: StateInfo, b: StateInfo) StateInfo {
    if (a.acc != b.acc) {
        return if (a.acc) a else b;
    }
    return .{
        .tt = @enumFromInt(@min(@intFromEnum(a.tt), @intFromEnum(b.tt))),
        .acc = a.acc,
    };
}

pub const RegexTokenizer = struct {
    const Self = @This();
    dfa: DFA,
    pub fn init(comptime rules: []const struct { TokenType, []const u8 }) !Self {
        var nfa = NFA.init(allocator, .{ .tt = .ignore, .acc = false });
        defer nfa.deinit();
        var subs: [rules.len]NFA.Sub = undefined;
        var i: usize = 0;
        while (i < rules.len) : (i += 1) {
            nfa.value = .{ .tt = rules[i][0], .acc = false };
            var rr = RegexReader.init(&nfa, rules[i][1]);
            subs[i] = try rr.read();
            nfa.states.items[subs[i].end].value.acc = true; // HACK
        }
        const sub = try nfa.choice(&subs);
        return .{
            .dfa = try nfa.toDFA(sub, mergeTokens),
        };
    }
    // no need to deinit
    pub fn nextToken(self: Self, buf: []const u8) Token {
        var state: usize = 0;
        var len: usize = 0;
        while (len < buf.len) : (len += 1) {
            const c = buf[len];
            if (self.dfa.getTransition(state, c)) |next_state| {
                state = next_state;
            } else {
                return .{
                    .kind = self.dfa.states.items[state].value.tt,
                    .len = @max(len, 1), // HACK
                };
            }
        }
        return .{
            .kind = self.dfa.states.items[state].value.tt,
            .len = @max(len, 1),
        };
    }
};

// Two interesting optimizations are possible here:
// - We could retokenize only the affected range.
// - We could use a clever data structure (e.g. B-trees augmented with sums of token lengths)
// All in all this would in theory allow updates in O(edit size) time.
// But we don't have working B-trees so we don't and this is O(text size) on every edit. Urgh.
pub const Tokenization = struct {
    const Self = @This();
    tokens: std.ArrayList(Token),
    pub fn init(text: []const u8, tokenizer: RegexTokenizer) !Self {
        var i: usize = 0;
        var tokens = std.ArrayList(Token).init(allocator);
        while (i < text.len) {
            const t = tokenizer.nextToken(text[i..]);
            try tokens.append(t);
            i += t.len;
        }
        return .{ .tokens = tokens };
    }
    pub fn deinit(self: *Self) void {
        self.tokens.deinit();
    }
    pub const Hint = struct {
        ti: usize = 0,
        sum: usize = 0,
    };
    pub fn getPreviousTokenStart(self: Self, i: usize) usize {
        var sum: usize = 0;
        var res: usize = 0;
        var j: usize = 0;
        while (sum < i and j < self.tokens.items.len) : (j += 1) {
            const token = self.tokens.items[j];
            if (token.kind != .ignore)
                res = sum;
            sum += token.len;
        }
        return res;
    }
    pub fn getNextTokenEnd(self: Self, i: usize) usize {
        var sum: usize = 0;
        var j: usize = 0;
        while (sum < i and j < self.tokens.items.len) {
            sum += self.tokens.items[j].len;
            j += 1;
        }
        while (j < self.tokens.items.len) : (j += 1) {
            sum += self.tokens.items[j].len;
            if (self.tokens.items[j].kind != .ignore) break;
        }
        return sum;
    }
    pub fn getToken(self: Self, i: usize, hint: *Hint) Token {
        while (hint.sum <= i and hint.ti < self.tokens.items.len) {
            hint.sum += self.tokens.items[hint.ti].len;
            hint.ti += 1;
        }
        return self.tokens.items[hint.ti - 1];
    }
};
txtbuf.zig 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
const std = @import("std");
const fs = std.fs;
const Path = []const u8;

const stdio = @cImport({
    @cInclude("stdio.h");
});

const Pos = @import("pos.zig");

const char = u8;

const allocator = std.heap.c_allocator;
const Allocator = std.mem.Allocator;

const ArrayList = std.ArrayList;

const Tokenization = @import("tok.zig").Tokenization;

pub const FileType = enum {
    plain,
    c,
    fn init(path: Path) FileType {
        return if (std.mem.endsWith(u8, path, ".c")) .c else .plain;
    }
};

fn tokenize(t: FileType, text: []const u8) !Tokenization {
    return Tokenization.init(text, switch (t) {
        .plain => try @import("plain.zig").tokenizer(),
        .c => try @import("c.zig").tokenizer(),
    });
}

pub const TextBuffer = struct {
    const Self = @This();
    chars: ArrayList(char),
    lineOffsets: ArrayList(usize), // where does the respective line end?
    fileType: FileType,
    tokens: Tokenization,
    pub fn empty() !Self {
        var chars = try ArrayList(char).initCapacity(allocator, 1);
        try chars.append('\n');
        return Self{
            .chars = chars,
            .lineOffsets = try calculateLineOffsets(chars.items),
            .tokens = try tokenize(.plain, chars.items),
            .fileType = .plain,
        };
    }
    pub fn open(path: Path) !Self {
        const file = try fs.cwd().openFile(path, .{ .mode = .read_only });
        const reader = file.reader();
        var chars = ArrayList(char).init(allocator);
        {
            defer file.close();
            try reader.readAllArrayList(&chars, 16384);
        }
        const ft = FileType.init(path);
        return .{
            .chars = chars,
            .lineOffsets = try Self.calculateLineOffsets(chars.items),
            .tokens = try tokenize(ft, chars.items),
            .fileType = ft,
        };
    }
    pub fn save(self: *TextBuffer, path: Path) !void {
        const old_ft = self.fileType;
        self.fileType = FileType.init(path);
        if (old_ft != self.fileType) {
            self.tokens.deinit();
            self.tokens = try tokenize(self.fileType, self.chars.items);
        }

        const dirname = fs.path.dirname(path);
        const basename = fs.path.basename(path);
        const tmpname = try std.mem.concat(allocator, u8, &.{ ".~sped-tmp-", basename });
        defer allocator.free(tmpname);
        const tmpath = try fs.path.join(allocator, if (dirname) |name| &.{ name, basename } else &.{basename});
        defer allocator.free(tmpath);
        const file = try fs.cwd().createFile(tmpath, .{});
        {
            defer file.close();
            try file.writeAll(self.chars.items);
        }
        try fs.cwd().rename(tmpath, path);
    }
    pub fn deinit(self: *Self) void {
        self.chars.deinit();
        self.lineOffsets.deinit();
        self.tokens.deinit();
    }
    /// Number of rows, always >= 1.
    pub fn nRows(self: Self) usize {
        return self.lineOffsets.items.len;
    }
    pub fn nCols(self: Self, row: usize) usize {
        return if (row == 0)
            self.lineOffsets.items[0]
        else
            self.lineOffsets.items[row] - self.lineOffsets.items[row - 1] - 1;
    }
    // TODO LFs (<=)
    pub fn inBounds(self: Self, pos: Pos) bool {
        return pos.row < self.nRows() and pos.col < self.nCols(pos.row);
    }
    fn rowStart(self: Self, row: usize) usize {
        return if (row == 0) 0 else self.lineOffsets.items[row - 1] + 1;
    }
    pub fn getIdx(self: Self, pos: Pos) usize {
        return self.rowStart(pos.row) + pos.col;
    }
    fn loLt(_: void, lhs: usize, rhs: usize) bool {
        return lhs < rhs;
    }
    /// O(log n)
    pub fn getPos(self: Self, idx: usize) Pos {
        const row = std.sort.lowerBound(usize, idx, self.lineOffsets.items, void{}, loLt);
        return .{ .row = row, .col = idx - self.rowStart(row) };
    }
    pub fn get(self: Self, pos: Pos) ?char {
        return if (self.inBounds(pos)) self.chars.items[self.getIdx(pos)] else null;
    }
    pub fn getRange(self: Self, from: usize, to: usize) []const u8 {
        return self.chars.items[from..to];
    }
    fn calculateLineOffsets(chars: []const u8) !ArrayList(usize) {
        var lineOffsets = ArrayList(usize).init(allocator);
        var i: usize = 0;
        while (std.mem.indexOfScalar(char, chars[i..], '\n')) |offset| {
            try lineOffsets.append(i + offset);
            i += offset + 1;
        }
        if (chars.len == 0 or i < chars.len) // no trailing newline
            try lineOffsets.append(chars.len); // note: no newline!
        return lineOffsets;
    }
    pub fn replaceRange(self: *Self, from: usize, len: usize, replacement: []const u8) !void {
        try self.chars.replaceRange(from, len, replacement);
        self.lineOffsets.deinit();
        self.lineOffsets = try calculateLineOffsets(self.chars.items);
        self.tokens.deinit();
        self.tokens = try tokenize(self.fileType, self.chars.items);
    }
};
util.zig ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const std = @import("std");
const Pos = @import("pos.zig");

pub fn getTtySize(tty: std.fs.File) !Pos {
    var size: std.c.winsize = undefined;
    size.ws_row = 60;
    size.ws_col = 80;
    const err = std.c.ioctl(tty.handle, std.c.T.IOCGWINSZ, &size);
    if (err != 0)
        return error.TerminalSizeUnknown;
    return .{ .row = size.ws_row, .col = size.ws_col };
}
build.zig 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
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "sped",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
        .link_libc = true,
    });

    b.installArtifact(exe);

    const run_cmd = b.addRunArtifact(exe);

    run_cmd.step.dependOn(b.getInstallStep());

    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    const run_step = b.step("run", "Run editor");
    run_step.dependOn(&run_cmd.step);

    const exe_unit_tests = b.addTest(.{
        .root_source_file = b.path("src/c.zig"),
        .target = target,
        .optimize = optimize,
        .link_libc = true,
    });

    const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);

    const test_step = b.step("test", "Run unit tests");
    test_step.dependOn(&run_exe_unit_tests.step);
}
build.zig.zon ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.{
    .name = "sped",
    .version = "0.0.0",
    .minimum_zig_version = "0.13.0",
    .dependencies = .{},
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
        // TODO license
    },
}
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
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
sped is a lightweight, primitive text editor

if first command line argument is a file that is opened

stuff marked with [ ] is not yet implemented

features:

- syntax highlighting (C only)
- [ ] good performance even for massive files

keybindings:

notation:

- Control: C
- Shift: S
- Arrow Keys: L, R, U, D

cursor movement:

- L / R / U / D: move cursor
- begin / end: move to begin / end of line
- C-begin / C-end: move to begin / end of file
- [ ] page up / page down: move cursor up / down one page
- C-L / C-R: move to previous / next token
- [ ] C-U / C-D:
    * if parentheses (tokens) are unmatched at cursor position:
      move to previous opening / subsequent closing parenthesis
    * otherwise:
      move to previous / subsequent smaller indentation level

selection:

- S-L / S-R / S-U / S-D: start or continue selection 
- C-c / C-x / C-v: copy / cut / paste
	* operate on the given line if nothing is selected
- C-a / C-t: select all / token

editing:

- character keys: insert character
- backspace: delete
- [ ] tab:
	* if preceding characters are whitespace (incl. newline): indent
	* otherwise: autocomplete token
- [ ] S-tab: dedent
- enter: insert newline (with indentation)
- C-z / C-y: undo / redo

meta:

- C-q: quit
- C-s: save file
- [ ] C-f: find
- [ ] C-r: replace
- [ ] C-g: goto
- [ ] C-i: help

round #61

submitted at
1 like

guesses
comments 0

post a comment


rps-royale.lua 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
-- y'all will guess me anyway but who cares,
-- scissors is winning rock paper scissors royale!
-- (i hope)
getmetatable"".__index = function(str, key)
  if type(key) == "number" then
    return (key >= 1 and key <= #str) and str:sub(key, key) or nil
  end
  return string[key]
end
local readline = io.lines()
while 1 do
  local grid = {}
  repeat
    local line = readline()
    if not line then break end -- being nice or something
    grid[#grid+1] = line
  until line == ""
  local x = assert(tonumber(readline()))
  local y = assert(tonumber(readline()))
  local dirs = {
    L = {-1, 0},
    R = {1, 0},
    U = {0, -1},
    D = {0, 1},
  }
  local function get_tile(tx, ty)
    return (grid[ty] or {})[tx] or "#"
  end
  local function murder(K, V)
    local function dist(mx, my)
      local res = 0
      local level = {{mx, my}}
      local visited = {[my] = {[mx] = true}}
      repeat
        local next_level = {}
        for _, pos in ipairs(level) do
          local px, py = table.unpack(pos)
          for dn, dp in pairs(dirs) do
            local dx, dy = table.unpack(dp)
            local nx, ny = px + dx, py + dy
            if ({[K..V]=1,[V..K]=1})[get_tile(px,py)..get_tile(nx,ny)] then
              return res
            end
            if get_tile(nx, ny) ~= "#" and not (visited[ny] or {})[nx] then
              visited[ny] = visited[ny] or {}
              visited[ny][nx] = true
              next_level[#next_level+1] = {nx, ny}
            end
          end
        end
        level = next_level
        res = res + 1
      until not level[1]
      return math.huge
    end
    local best_move, min_dist = nil, math.huge
    for dn, dp in pairs(dirs) do
      local dx, dy = table.unpack(dp)
      if ({[K..V]=1,[V..K]=1})[get_tile(x,y)..get_tile(x+dx,y+dy)] then
        print"I"
        print(dn)
        return
      end
      local can_dist = dist(x + dx, y + dy)
      if can_dist < min_dist then
        best_move, min_dist = dn, can_dist
      end
    end
    if best_move then
      print"M"
      print(best_move)
      return
    end
    print"P" -- there is nothing we can do
  end
  if table.concat(grid):find"R" then
    murder("P", "R")
  else
    murder("S", "P")
  end
end

round #56

submitted at
0 likes

guesses
comments 0

post a comment


sus.zig 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
                                 const std=@import("std");const ArrayList                 
                                 =std.ArrayList;const Doer=struct{                        
                                 const State=enum{Normal,JustTainted                      
                        ,Tainted,TaintedGroup,Ignore,};const SyntaxError=error            
                        .InvalidSyntax;buf:ArrayList(u8),stk:ArrayList(usize              
                        ),stateStk:ArrayList(State),state:State,concatBegin               
                        :usize,pub                     fn init(alloc:anytype)Doer         
                        {return Doer                  {.buf=ArrayList(u8).init            
                        (alloc),.                     stk=ArrayList(usize).init           
                     (alloc),.         stateStk=ArrayList(State).init(alloc),.state       
                     =.Normal,         .concatBegin=0,};}pub fn startConcat(self:         
                     *Doer)void        {self.eraseConcat();self.state=.Normal;}pub        
                     fn eraseConcat (self:*Doer)void                    {self.buf.shrinkRetainingCapacity
                     (self.concatBegin);}pub fn pushState               (self:*Doer)!void 
                     {try self      .stk.append(self                    .concatBegin);try 
      self.stateStk.append(         self.state);self                             .concatBegin
      =self.buf.items.len;}         pub fn popState                              (self:   
      *Doer)!void{self.concatBegin  =self.stk.popOrNull                          () orelse 
   return SyntaxError;self.         state=self.stateStk.popOrNull() orelse unreachable;}   
   pub fn taint(self:*Doer)         void{self.eraseConcat();self.state=.Tainted;}pub fn   
   bufChar(self:*Doer,c:u8)         !void{try self.buf.append(c);}pub fn do(self:*Doer,   
   in:anytype     )![]const             u8{while(in.readByte())|c|{switch(self.state){.   
   Normal=>switch (c){'\\'=>           try self.bufChar(try in.readByte()),'|'=>self.state
   =.Ignore,      '*'=>{},'['          =>{if(try in.readByte()!=']')return SyntaxError;   
   self.state     =.JustTainted           ;},']'=>return SyntaxError,'('=>try self.pushState
   (),')'=>try     self.popState          (),else=>try self.bufChar(c),},.JustTainted     
   =>switch(      c){'\\'=>               {try in.skipBytes(1,.{});self.taint();},'*'     
   =>self.state   =.Normal,                                                '|'=>self      
   .startConcat   (),'('=>{                                                self.taint     
   ();try self    .pushState                                               ();},')'=>     
   {self.         eraseConcat                                              ();self.state  
   =.TaintedGroup ;},else=>                                                self.taint     
   (),},.         Tainted=>                                                switch(c)      
   {'\\'=>        try in.skipBytes                                         (1,.{}),'|'    
   =>self         .startConcat                                             (),'('=>try    
   self.pushState (),')'=>{                                                self.eraseConcat
   ();self.state  =.TaintedGroup                                           ;},else=>      
   {},},.TaintedGroup=>switch                                              (c){'*'=>      
   try self.      popState(                                                ),'|'=>{try    
   self.popState  ();self.startConcat                                      ();},'('=>     
   {try self.popState();self                                               .taint();      
   try self.pushState();},')'                                              =>{try self    
      .popState();self.state              =.TaintedGroup;},'\\'=>{try    in.skipBytes     
      (1,.{});try self.popState           ();self.taint();},else=>{try   self.popState    
      ();self.taint();}},.Ignore          =>switch(c){'\\','['=>try in  .skipBytes        
                     (1,.{}               ),']'=>return SyntaxError     ,'('=>try         
                     self.pushState       (),')'=>try  self.popState    (),else=>         
                     {},},}               }else|err   |{if(err!=error   .EndOfStream      
                     )return               err;}switch(self.state       ){.Normal         
                     ,.Ignore             =>return self.buf.toOwnedSlice(),else=>         
                     return                SyntaxError,}}};pub fn        main()!void      
                     {const stdout=std.io.getStdOut   ().writer();var bufout=std          
                     .io.bufferedWriter(stdout);const  stdin=std.io.getStdIn()            
                     ;var bufin=std.io.bufferedReader (stdin.reader());var arena          
                     =std.heap.ArenaAllocator.init                                        
                     (std.heap.page_allocator);defer                                      
                     arena.deinit();var doer=Doer                                         
.init(arena.allocator());try bufout.writer().writeAll(try doer.do(bufin.reader()));try bufout.flush();}

round #55

submitted at
0 likes

guesses
comments 0

post a comment


lyric.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
from sys import stdin

M = {
    '+' : ((1, 0),
           (0, 1)),
    '/' : ((0,-1),
           (-1,0)),
    '\\': ((0, 1),
           (1, 0)),
}

L = tuple(map(str.rstrip, stdin.readlines()))

W, H = S = max(map(len, L)), len(L)

def I(p):
    x, y = p
    return y in range(H) and x in range(len(L[y])) and L[y][x] in M

def D(v, w):
    return sum(x * y for x, y in zip(v, w))

def F(p, v):
    x, y = p
    m = M[L[y][x]]
    w = tuple(map(lambda r: D(r, v), m))
    return (x + w[0], y + w[1]), w

def P(p, v):
    o = p
    while I(p):
        p, v = F(p, v)
    if abs(D(o, v) - D(p, v)) >= abs(D(S, v)) >= 8:
        exit(1)

for x in range(W):
    P((x, 0), (0, 1))
    P((x, H-1), (0, -1))

for y in range(H):
    P((0, y), (1, 0))
    P((W-1, y), (-1, 0))

def C(p):
    for v in ((0, 1), (0, -1), (1, 0), (-1, 0)):
        q = set()
        while I(p):
            q.add(p)
            p, v = F(p, v)
            if p in q:
                exit(1)

for y, l in enumerate(L):
    for x in range(len(l)):
        C((x, y))

round #54

submitted at
0 likes

guesses
comments 0

post a comment


bnuuy.tar.paq8px208fix1 data

round #53

submitted at
2 likes

guesses
comments 0

post a comment


.php 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
#!/c/Program\ Files/PHP/php.exe
<?php
ini_set('evil_bit', 1);
$a=[];
while ($b=rtrim(fgets(STDIN), "\n\r"))
	{ $cs=array();
	  preg_match_all('/./u',$b,$cs);
	  $a[]=$cs[0];
	}
function d() {
global $a;
foreach ($a as $r)
{
	foreach ($r as $c)
		echo $c;
	echo "\n";
}
}
function ass($c) {
	if ($c) {
		return;
	}
	echo "invalid!!!";
	exit(69);
}
class RectAngle
{
	public readonly int $x1, $x2, $y1, $y2;
	public function __construct($x1, $x2, $y1, $y2) {
		$this->x1 = $x1;
		$this->x2 = $x2;
		$this->y1 = $y1;
		$this->y2 = $y2;
    }
    public function loop($f) {
		for ($x = $this->x1; $x <= $this->x2; $x++)
			for ($y = $this->y1; $y <= $this->y2; $y++)
				$f($x,$y);
    }
    public function cr() {
    	global $a;
    	$a[$this->y1][$this->x1]=$a[$this->y1][$this->x2]=$a[$this->y2][$this->x1]=$a[$this->y2][$this->x2]=' ';
    }
    public function inp($x,$y) {
    	return $x >= $this->x1 && $x <= $this->x2 && $y >= $this->y1 && $y <= $this->y2;
    }
}
if (!($h=count($a))) {
	exit;
}
$w=count($a[0]);
$rn=0;
$ls=[
	'│' => "UD",
	'─' => "LR",
	'┴' => "LRU",
	'┬' => "LRD",
	'┤' => "LUD",
	'├' => "RUD",
	'┌' => "DR",
	'┐' => "LD",
	'└' => "UR",
	'┘' => "LU",
];
$ds=[
	"L" => [-1,0],
	"R" => [1,0],
	"U" => [0,-1],
	"D" => [0,1],
];
$br=[];
foreach ($a as $y=>$r) {
	foreach ($r as $x=>$c) {
		switch ($c) {
			case '┌':
				$xn = $x;
				while (++$xn < $w && $r[$xn] != '┐');
				if ($xn==$w) continue 2;
				$yn = $y;
				while (++$yn < $h && $a[$yn][$x] != '└');
				if ($yn==$h) continue 2;
				if ($a[$yn][$xn] != '┘') continue 2;
				$re=new RectAngle($x,$xn,$y,$yn);
				$re->cr();
				$con=[++$rn=>true];
				$fl = function($x,$y) use ($re,&$br,$rn,$ds,$ls,$w,$h,&$con) {
					global $a;
					$br[$y][$x] = $rn;
					$in=0;
					$dv=null;
					ass(array_key_exists($a[$y][$x],$ls));
					foreach (str_split($ls[$a[$y][$x]]) as $dc) {
						$dw=$ds[$dc];$xm=$x+$dw[0];$ym=$y+$dw[1];
						if ($re->inp($xm,$ym)) $in++;
						else $dv = $dw;
					}
					ass($in==2);
					$a[$y][$x]=' ';
					if ($dv==null) return;
					$kill=[];
					while (1) {
						$x+=$dv[0];$y+=$dv[1];
						$kill[]=[$x,$y];
						ass((new RectAngle(0,$w-1,0,$h-1))->inp($x,$y));
						$re2 = null;
						if (array_key_exists($y,$br) && array_key_exists($x,$br[$y]))
							$re2 = $br[$y][$x];
						if ($re2!=null) {
							ass(!array_key_exists($re2,$con));
							$con[$re2]=true;
							foreach ($kill as $k)
								$a[$k[1]][$k[0]]=' ';
							break;
						}
						ass(array_key_exists($a[$y][$x],$ls));
						$sl = strlen($ls[$a[$y][$x]]);
						if ($sl==3) break;
						ass($sl==2);
						$dv2=null;
						foreach (str_split($ls[$a[$y][$x]]) as $dc) {
							$dvc=$ds[$dc];
							if ($dvc[0]!=-$dv[0] || $dvc[1]!=-$dv[1])
								$dv2=$dvc; #there is no going back
						}
						$dv=$dv2;
					}
				};
				(new RectAngle($x+1, $xn-1, $y+1, $yn-1))->loop(function($x,$y) {
					 global $a;
					ass($a[$y][$x] == ' ');
				});
				foreach ([
					new RectAngle($x+1, $xn-1, $y, $y),
					new RectAngle($x+1, $xn-1, $yn, $yn),
					new RectAngle($x, $x, $y+1, $yn-1),
					new RectAngle($xn, $xn, $y+1, $yn-1)
				] as $rl) $rl->loop($fl);
				break;
			case '│':
			case '─':
			case '┴':
			case '┬':
			case '┤':
			case '├':
			case '┐':
			case '└':
			case '┘':
			case ' ':
				break; # a ok
			default:
				ass(0);
		}
	}
}
foreach ($a as $r)
	foreach ($r as $c)
		ass($c==' ');
?>

round #52

submitted at
0 likes

guesses
comments 0

post a comment


thuemorseq.hs Unicode text, UTF-8 text
1
2
3
4
5
6
import Data.Bits (popCount, (.&.))
import Data.Function (on)
naive = [0] ++ go [0] where go seq = let inv = map (1-) seq in inv ++ go (seq ++ inv)
parity = go (0 :: Integer) where go i = popCount i .&. 1 : go (succ i)
main = if ((==) `on` take 1000) naive parity then putStrLn "Test passed" else error "Test failed"
-- 🥱

round #51

submitted at
1 like

guesses
comments 0

post a comment


tm.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
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#define mapass(v,f,...) v=f(v,##__VA_ARGS__)
enum C {Z,O,E};
const char L=-1,R=1;
typedef struct {char m;enum C w;int n;} T;
const T sts[7][3]={{
	{L,E,1},
	{L,E,1},
	{L,E,5},
},{
	{L,O,4},
	{L,Z,1},
	{R,E,2},
},{
	{R,O,3},
	{R,O,2},
	{R,O,0},
},{
	{R,Z,3},
	{R,Z,2},
	{R,Z,0},
},{
	{L,Z,4},
	{L,O,4},
	{R,E,6},
},{
	{L,Z,5},
	{L,O,5},
	{R,E,-1},
},{
	{R,E,2},
	{R,E,2},
	{R,E,2},
}};
char *dostuff(char *t)
{
	int s=0;
	++t;
	do {
		T tr=sts[s][*t];
		*t=tr.w; t+=tr.m; s=tr.n;
	} while (s!=-1);
	return t;
}
main()
{
	char c,*t=malloc(1);
	t[0]=E;
	int nt=1,ct=1;
	while (EOF!=(c=getchar())) {
		if (nt==ct) assert(mapass(t,realloc,ct<<=1));
		t[nt++]=Z;
	}
	if (nt+2>ct) assert(mapass(t,realloc,ct+2));
	t[nt++]=E;
	t[nt++]=E;
	assert(feof(stdin));
	mapass(t,dostuff);
	for(char *e=t+nt;t!=e&&*t!=E;++t)
		assert(EOF!=putchar('0'+*t));
}

round #50

submitted at
0 likes

guesses
comments 0

post a comment


thing.java 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
import java.util.*;import java.util.regex.Pattern;import java.util.stream.*;import java.io.*;
// da spek sed "Your complete function may produce any amount of output, although the result should depend on text_so_far given some corpus."
// dis meks a infinte strem o'. do smth liek java thing.java "I like fish" <bible.txt | head -c 1000 o jst ctrl^C idk
class Thing{
public static void main(String... args){
final var corpus=new BufferedReader(new InputStreamReader(System.in)).lines().collect(Collectors.joining("\n"));
final var text_so_far=args[0];
final var n=3;
var next=new HashMap<String,ArrayList<String>>();
var p=Pattern.compile("\\w+");
var m=p.matcher(corpus);
var prevs=new ArrayList<String>();
prevs.add("");
while(m.find()){
var tok=m.group();
var ctx=prevs.get(prevs.size()-1);
for(var i=1;i<=Math.min(prevs.size()-1,n);i++){
var nexts=next.getOrDefault(ctx,new ArrayList<>());
nexts.add(tok);
next.put(ctx,nexts);
ctx=" "+prevs.get(prevs.size()-1-i)+" "+ctx;
}
prevs.add(tok);
}
var r=new Random();
m=p.matcher(text_so_far);
var ks=prevs;
prevs=new ArrayList<String>();
for (var i=0;i<n;i++)
prevs.add(ks.get(r.nextInt(ks.size())));
while (m.find())
prevs.add(m.group());
while(true){
var ctx = prevs.get(prevs.size()-1);
var choisez = new ArrayList<String>();
for(var j=1;j<=Math.min(prevs.size()-1,n);j++) {
choisez.addAll(next.getOrDefault(ctx, new ArrayList<>()));
ctx = prevs.get(prevs.size()-1-j)+" "+ctx;
}
if(choisez.isEmpty()){
for(var i=0;i<n;i++)
prevs.add(ks.get(r.nextInt(ks.size())));
}
var w = choisez.get(r.nextInt(choisez.size()));
System.out.print(" "+w);
prevs.add(w);
}
}
}

round #49

submitted at
0 likes

guesses
comments 0

post a comment


crisp.rkt 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
#lang racket
(define (proceed-parse in) (read-char in) (parse in))
(define (assert cond) (when (not cond) (raise "assertion failed")))
(define (expect in c) (assert (eqv? c (read-char in))))
(define (read-atom in)
  (let [(c (peek-char in))]
    (if (or (char-whitespace? c) (char=? #\) c)) '()
        (begin (read-char in) (cons c (read-atom in))))))
(define (skip-line in)
  (if (eqv? #\newline (read-char in)) (void) (skip-line in)))
(define (parse in)
  (let [(c (peek-char in))]
    (cond
      [(or (eof-object? c) (char=? #\) c)) '()]
      [(char=? #\# c) (begin (skip-line in) (parse in))]
      [(char-whitespace? c) (proceed-parse in)]
      [(char=? #\( c) (let [(r (proceed-parse in))]
                        (begin (expect in #\)) (cons r (parse in))))]
      [else (cons (let [(s (list->string (read-atom in)))]
                    (or (string->number s) (list 'name s)))
                  (parse in))])))
(define (fnify f)
  (lambda (args ctx) (f (eval-args args ctx))))
(define (thunk args ctx)
  (fnify (lambda (_) (last (eval-args args ctx)))))
(define (my-lambda args ctx)
  (letrec [(param (begin (assert (eqv? 'name (caar args))) (cadar args)))
           (body (cdr args))
           (f (lambda (xs)
                (if (null? xs) f
                    (let* [(new-ctx (hash-set ctx param (car xs)))
                           (res (last (eval-args body new-ctx)))]
                      (if (null? (cdr xs)) res
                          (res (cdr xs) ctx))))))]
    (fnify f)))
(define (my-quote args _) args)
(define (fnify-n n f)
  (letrec [(l (lambda (args ctx)     
                (if (null? args) l
                    (let [(arg (eval (car args) ctx))]
                      (if (= n 1)
                          (if (null? (cdr args))
                              (f arg)
                              ((f arg) (eval-args (cdr args) ctx)))
                          ((fnify-n (- n 1) (curry f arg)) (cdr args) ctx)))
                    )))] l))
(define my-t (fnify-n 2 (lambda (x y) x)))
(define my-f (fnify-n 2 (lambda (x y) y)))
(define (my-<= n m)
  (cond
    [(eq? n m) my-t]
    [(and (number? n) (number? m)) (if (<= n m) my-t my-f)]
    [(null? n) (if (pair? m) my-t my-f)]
    [(and (pair? n) (pair? m))
     (cond
       [(not (my-<= (car n) (car m))) my-f]
       [(not (my-<= (car m) (car n))) my-t]
       [else (my-<= (cdr n) (cdr m))])]
    [else my-f]))
(define (d args)
  (display args)
  (newline))
(define (my-macro margs mctx)
  (lambda (args ctx)
    (eval (last (eval-args margs (hash-set ctx "..." args))) ctx)))
(define predefs
  (hash
   ;; builtins (mostly inherited from racket)
   "d" (fnify d)
   "ib" (fnify (lambda (_) (char->integer (read-char))))
   "ob" (fnify-n 1 (lambda (c) (write-char (integer->char c))))
   "do" (fnify last)
   "car" (fnify-n 1 car)
   "cdr" (fnify-n 1 cdr)
   "cons" (fnify-n 2 cons)
   "list" (fnify identity)
   "-" (fnify-n 1 -)
   "+" (fnify-n 2 +)
   "*" (fnify-n 2 *)
   "<=" (fnify-n 2 my-<=)
   "t" my-t
   "f" my-f
   ;; special forms
   "\\\\" thunk
   "\\" my-lambda
   "'" my-quote
   "$" my-macro)) 
(define (eval-args args ctx)
  (map (lambda (arg) (eval arg ctx)) args))
(define (eval expr ctx)
  (if (pair? expr)
      (let [(f (car expr)) (args (cdr expr))]
        (if (eqv? f 'name) (hash-ref ctx (car args))
            ((eval f ctx) args ctx)))
      expr))
(define (get-file-name)
  (command-line
   #:program "crisp"
   #:args (filename)
   filename))
(define (maybe-display v)
  (when (not (eq? v (void))) (display v)))
(maybe-display (eval
                (cons (list 'name "do")
                      (call-with-input-file (get-file-name) parse))
                predefs))
stuff.crisp 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
# she bangs
# (all lines starting with # actually) are ignored

# run this using "racket crisp.rkt stuff.crisp"

# d is a debug print for a list of arguments
(d 1 2 3)
# (ib) reads a byte, (ob b) writes a byte
(ob 65)
# (d (ib))
# basic math works; - is unary; +, * are strictly binary, fold them yourself
(d (+ (+ (* 4 5) (* 2 3)) (- 1)))
# lambdas (single parameter only)
(d ((\ x (\ y (+ x y))) 1 2))
# you can quote stuff, albeit unusually
(d (' + 1 2))
# automatic currying
(d (+ 1))
(d 42)
(d ((+ 1) 2))
(d 69)
# "thunks" (paramless lambdas)
(d ((\\ (+ 1 2))))
# we've got cons, car, cdr, list
(d (cons 1 2))
(d (car (cons 1 2)))
(d (cdr (cons 1 2)))
(d (')) # empty list
(d (cons 1 ('))) # list with one elem
(d (list 1 2 3))
# bools are just functions
(d (t 1 0))
(d (f 1 0))
(d (<= 1 2))
(d ((<= 1 2) 1 0))
# <= is special: it compares lexicographically and can be used to test for nil
(d ((<= (') (')) 1 0))
(d ((<= (') (' 1 2)) 1 0))
(d ((<= (' 1 2) (')) 1 0))
(d ((<= (' 1 2) (' 1 2)) 1 0))
# this is enough for a simple recursive fibonacci already
(d ((\ f (f f 10))
	(\ f (\ n (((<= n 1)
		(\\ n)
		(\\ (+ (f f (+ n (- 1))) (f f (+ n (- 2)))))))))))
# multiple things are awkward here:
# - nesting of lambdas
# - no let(rec)
# - thunks
# it's time to introduce "macros". macros are like functions,
# except they operate on the raw sexprs rather than the evaluated sexprs.
# $ is like \, but the parameter is always called ... and is implicit.
# "macro" "expansion" happens at runtime.
(d (($ (car ...)) 1 2 3))
(($ (d (list (car (' d)) ...)) (cons (car (' d)) ...)) 1 2 3)
(($ ...
	(list (list
		(car ...)
		(list (car (' \\)) (car (cdr ...)))
		(list (car (' \\)) (car (cdr (cdr ...)))))))
	t (d 1) (d 0))
# macros to solve the other problems are left as exercises to the reader

round #48

submitted at
0 likes

guesses
comments 0

post a comment


entry.ts ASCII text
1
export function entry(h:string,n:string):[number,number]|null{for(let g=1;n.length*g<=h.length;++g){const m=RegExp(n.split('').map(c=>(/[.*+?^${}()|[\]\\]/.test(c)?'\\':'')+c).join(`.{${g-1}}`)).exec(h);if(m)return[m.index,--g]}return null}// dis  onli wroks on WIDE CHARS because YES

round #47

submitted at
0 likes

guesses
comments 0

post a comment


LIS.groovy 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
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
/*
    Efficient longest increasing subsequence finding.
    Written for Code Guessing Round 47 (https://cg.esolangs.gay/47/).
    Input: Sequence of integers delimited by spacing, ex.: 4 5 2 6 3 5
    Output: Subsequence in the same format, ex.: 4 5 6

    Code may contain minor inconsistencies;
    the author was initially trying to conceal their style (this was for code guessing, after all)
    but then effectively backpedaled on that too.

    The author recommends using at least a gigabyte of IDE bloat to have a half-decent Groovy experience.
 */

class BigIntTree implements Iterable<Iterable<Integer>> {
    class Node {
        List<Integer> is
        Node left, right
    }
    Node root

    void set(BigInteger k, int i) {
        Node node = root ?= new Node()
        for (int j = k.bitLength() - 1; j > -1; --j)
            node = k.testBit(j) ? (node.right ?= new Node()) : (node.left ?= new Node())
        (node.is ?= []).add(i)
    }
    // Breadth-first traversal of the tree gives lets us traverse the keys in sorted order
    // in linear time in the total number of digits in the input
    @Override
    Iterator<Iterable<Integer>> iterator() {
        List<Node> lvl = root == null ? [] : [root]
        int i = 0
        return new Iterator<Iterable<Integer>>() {
            @Override
            boolean hasNext() {
                return !lvl.isEmpty()
            }

            @Override
            Iterable<Integer> next() {
                List<Integer> vs
                do {
                    vs = lvl.get(i).is
                    if (++i >= lvl.size()) {
                        List<Node> nlvl = []
                        for (nd in lvl) {
                            if (nd.left != null) nlvl.add(nd.left)
                            if (nd.right != null) nlvl.add(nd.right)
                        }
                        lvl = nlvl
                        i = 0
                    }
                } while (vs == null)
                return vs
            }
        }
    }
}

class Subseq {
    class Node {
        int i
        Node prev

        Node(int i, Node prev) {
            this.i = i
            this.prev = prev
        }
    }
    int len
    Node root
    static Subseq EMPTY = new Subseq()

    Subseq() {}

    Subseq(Subseq p, int i) {
        len = (p == null ? 0 : p.len) + 1
        root = new Node(i, p.root)
    }

    BigInteger[] apply(BigInteger[] seq) {
        def res = new BigInteger[len]
        def node = this.root
        for (def i = len - 1; i > -1; i--) {
            res[i] = seq[node.i]
            node = node.prev
        }
        return res
    }
}

class SegmentTree {
    class Node {
        Subseq subseq = Subseq.EMPTY
        Node left, right

        Node(int n) {
            assert (n > 0)
            if (n == 1) return
            def mid = n >> 1
            left = new Node(mid)
            right = new Node(n - mid)
        }
        // Get best subseq s with max(s) <= n
        Subseq get(int w, int n) {
            assert n <= w
            if (w == n)
                return subseq
            def mid = w >> 1
            if (n <= mid)
                return left.get(mid, n)
            def ssl = left.subseq
            def ssr = right.get(w - mid, n - mid)
            return (ssr.len > ssl.len) ? ssr : ssl
        }
        // Set new best subseq s for max(s) <= n
        void set(int w, int n, Subseq ss) {
            assert n <= w
            if (w == n) {
                if (ss.len > subseq.len)
                    subseq = ss
                return
            }
            if (ss.len > subseq.len)
                subseq = ss
            def mid = w >> 1
            if (n <= mid) {
                left.set(mid, n, ss)
                return
            }
            // Note: We can't install this for left, since left has a stricter bound
            right.set(w - mid, n - mid, ss)
        }
    }
    Node root
    int n

    SegmentTree(int n) {
        root = new Node(this.n = n)
    }

    Subseq get(int n) {
        return root.get(this.n, n)
    }

    Subseq set(int n, Subseq ss) {
        return root.set(this.n, n, ss)
    }
}

static BigInteger[] solve(BigInteger[] seq) {
    if (seq.length == 0) return new BigInteger[0]
    def t = new BigIntTree()
    for (int i = 0; i < seq.length; ++i)
        t.set(seq[i], i)
    // Remove gaps in the ordering.
    // This is important for achieving good time complexity.
    int o = 0
    def ordinals = new int[seq.length]
    for (is in t) {
        ++o
        for (i in is)
            ordinals[i] = o
    }
    // Do the "greedy" / "dynamic programming" solving
    // using a segment tree of best subsequences for upper bounds of the last element.
    def st = new SegmentTree(o)
    for (int i = 0; i < ordinals.length; ++i) {
        def n = ordinals[i]
        def ss = n == 1 ? Subseq.EMPTY : st.get(n - 1)
        st.set(n, new Subseq(ss, i))
    }
    return st.get(o).apply(seq)
}

static BigInteger[] read() {
    def sc = new Scanner(System.in)
    List<BigInteger> s = []
    while (sc.hasNext())
        s << new BigInteger(sc.next())
    return s.toArray(new BigInteger[s.size()])
}

static void write(BigInteger[] subseq) {
    print(subseq[0])
    for (int i = 1; i < subseq.length; i++) {
        print(" ")
        print(subseq[i])
    }
    println()
}

static void main(String[] args) {
    write(solve(read()))
}

/*
    Runtime analysis:

    Let the input be a sequence of n numbers encoded in some base
    (say, binary, or decimal, with at least one digit per number), delimited by some delimiter.
    Then the length of the input is O(m), where m is the total count of digits in the input (in any fixed base).
    Constructing the prefix tree to sort the numbers is O(m) then,
    as is traversing the prefix tree in level-order (breadth-first, left to right);
    replacing numbers with their ordinals is O(m) as well
    (incrementing the ordinal o would be amortized constant time even if o were a bigint).

    For each distinct ordinal, there needs to be a distinct number in the input.
    To encode k distinct numbers, you need Θ(log(k)) bits (on average) for each number.
    This implies an input length of Θ(n log(k)) (in bits, bytes, characters, whatever, as long as it's fixed size).
    (This also imposes an upper bound of O(log n) on the length of each ordinal in bits,
    which is why this implementation elects to represent ordinals as "just integers":
    larger sequences couldn't be represented using Java data structures anyways.)

    The next step maintains a "segment tree" which maps maxima to longest subsequences
    using only elements encountered so far (a prefix of the sequence).
    Since the largest value is the largest ordinal, this segment tree has depth log(k).
    For each element in the sequence, one get and set operation are issued.
    Since these operations can both easily be seen (*) to be O(log(k)),
    we obtain O(n log(k)), which is true linear time in the input size (in bits, not in integer registers).

    (*) For a simplified analysis, assume a perfect binary segment tree (that is, k is a power of two).
    We presume big integers (which this implementation does not use for aforementioned reasons,
    but which are nevertheless relevant for runtime analysis).
    Then the comparison `w == n` would not be constant time,
    but for each bit it has to compare, it saves having to dive a layer deeper, so it is fine.
    Since we split perfectly in the middle, `n <= mid` would only have to look at the most significant bit.
    `w - mid` / `n - mid` effectively just removes that most significant bit, thus is also constant time.

    A proof of correctness will be supplied when the author finds the time and motivation.
    For now, you will have to take the author's "trust me bro" for it.

    Exercises left to the reader:

    * Test this thoroughly to convince yourself of the correctness.
    * Constant-factor optimization of the binary tree / prefix tree:
      "Compress" long paths of multiple bits (trie -> patricia tree)
    * Constant-factor optimization of the segment tree:
      Store it in an array, avoids many heap allocations, improves cache locality.
    * Rewrite this in a "better" programming language.
 */

round #46

submitted at
0 likes

guesses
comments 0

post a comment


README.rst 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
==========================
CG 46: Find Candidate Keys
==========================

Input
=====

**Tab-Separated Values** (TSV) on standard input as Olive intended.
The escape sequences ``\t``, ``\n``, ``\r`` and ``\\`` are supported.

Example
-------

Here is the example table in TSV format::

	contest	date held	winner	winner's second name
	dog fight	oct 17 2000	discarding sabot	sabot
	cat-off	jul 01 2001	palm tree oil	tree
	rat duel	oct 05 2001	cart of iron	of
	rat duel	mar 21 2006	cart of iron	of
	shark race	mar 21 2006	linguist	NULL

Output
======

For consistency, the output is also **pseudo-TSV** that is written to standard output.

Edge cases:

* The empty set of columns is represented by an empty line.
* The empty set of candidate keys is represented by no lines being printed.

Example
-------

Here is the output for the example table::

	date held	winner's second name
	date held	winner
	contest	date held
find_candidate_keys.clj 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
;; © 2042 Kim Apr
(ns find_candidate_keys
  (:require [clojure.string :as str]
            [clojure.set :as set]))
(def escs {"\\t" "\t" "\\n" "\n" "\\r" "\r" "\\\\" "\\"})
(defn rd-row [line]
  (map
   (fn [s] (str/replace s #"\\." (fn [c] (escs c c))))
   (str/split line #"\t")))
(defn rd-tsv []
  (let [ln (read-line)]
    (if (nil? ln) nil
           (cons (rd-row ln) (rd-tsv)))))
(defn transp [rws]
  (let [rst-rws (rest rws)]
    (if (empty? rst-rws)
         (map list (first rws))
        (map cons (first rws)
             (transp rst-rws)))))
(defn ntrvl [col]
  (filter
   ;; clj-kondo WILL lie to you!!!
   (fn [cl] (not (empty? (rest cl))))
   col))
(defn cl-add [cl i el] (assoc cl el (conj (cl el #{}) i)))
(defn eq-cls [col]
   (vals
    (reduce
     (fn [cl pair]
       (let [[i el] pair] (cl-add cl i el)))
      {}
     (map-indexed vector col))))
(defn mapify-eq-cls [cls]
  (reduce
   (fn [map cl]
     (reduce
      (fn [map i] (assoc map i cl))
      map
      cl))
   {}
   cls))
(defn merge-eq-cls [cls1 cls2]
  (let [m2 (mapify-eq-cls cls2)]
    (reduce
     (fn [cls cl1]
       (reduce
        (fn [cls cl2] (cons (set/intersection cl1 cl2) cls))
        cls
        (set (remove nil? (map m2 cl1)))))
     '()
     cls1)))
(defn map-subseq [f seq]
  (if (empty? seq) '()
      (cons (f seq) (map-subseq f (rest seq)))))
(defn cands [keys cnds]
  (if
   (empty? cnds) keys
   (reduce
    (fn [keys cnd]
      (if (empty? (cnd :eq))
        (cons (rest (reverse (map (comp first first) (cnd :cols)))) keys)
        (cands
         keys
         (remove nil? (map-subseq
          (fn [col]
            (let [mrgd (ntrvl (merge-eq-cls
                               (cnd :eq)
                               (ntrvl (eq-cls (rest (first col))))))]
              (if (= (cnd :eq) mrgd) nil
                  {:cols (cons col (cnd :cols)) :eq mrgd})))
          (rest (first (cnd :cols))))))))
    keys
    cnds)))
(defn cnd-empty [cols]
  (list {:cols (list (cons '() cols))
         :eq (ntrvl (list (set (range (count (rest (first cols)))))))}))
(def unescs (set/map-invert escs))
(defn wr-row [cnd]
  (println
   (str/join
    "\t"
    (map
     ;; It is with great disgust that I have to inform you that `.` in Java
     ;; matches "any character (may or may not match line terminators)"
     (fn [s] (str/replace s #".|\r|\n" (fn [c] (unescs c c)))) cnd))))
(defn wr-tsv [cnds]
  (run! wr-row cnds))
(wr-tsv (cands '() (cnd-empty (transp (rd-tsv)))))

round #45

submitted at
0 likes

guesses
comments 0

post a comment


bot.nim 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
import std/bitops
import std/strutils
import std/options

type
    X = range[0'u8..6'u8]
    Y = range[0'u8..5'u8]
    Stack = set[Y]
    Mask = array[X, Stack]
    Board = tuple[ours: Mask, theirs: Mask]

type RowBitset = range[0'u8..127'u8]

proc winRowUtil (row: RowBitset): bool =
    (row and 0b0001111) == 0b0001111 or
    (row and 0b0011110) == 0b0011110 or
    (row and 0b0111100) == 0b0111100 or
    (row and 0b1111000) == 0b1111000

var winRows: set[RowBitset]
for i in RowBitset(0)..127'u8:
    if winRowUtil(i):
        winRows.incl i

proc winRow (row: RowBitset): bool =
    row in winRows

proc row (m: Mask, y: Y): RowBitset =
    for x in X(0)..6:
        if y in m[x]:
            result = result or (1'u8 shl x)

proc ascDiag (m: Mask, x: X, y: Y): RowBitset =
    var j = 0'u8
    for i in -int8(x.min(y))..int8((6-x).min(5-y)):
        if Y(int8(y) + i) in m[X(int8(x) + i)]:
            result = result or (1'u8 shl j)
        j += 1

proc descDiag (m: Mask, x: X, y: Y): RowBitset =
    var j = 0'u8
    for i in -int8((6-x).min(y))..int8(x.min(5-y)):
        if Y(int8(y) + i) in m[X(int8(x) - i)]:
            result = result or (1'u8 shl j)
        j += 1

proc winMove (m: Mask, x: X, y: Y): bool =
    winRow(cast[RowBitset](m[x])) or
    winRow(m.row(y)) or
    winRow(m.descDiag(x, y)) or
    winRow(m.ascDiag(x, y))

proc top (board: Board, x: X): Option[Y] =
    let used = cast[uint8](board.ours[x] + board.theirs[x])
    if used == 0:
        return Y(0).some
    let y = 8 - used.countLeadingZeroBits
    if y > 5:
        return Y.none
    return Y(y).some

#[
import std/unittest
suite "bot":
    test "winRow":
        check(winRow 0b1111000)
        check(winRow 0b0111100)
        check(winRow 0b0011110)
        check(winRow 0b0001111)
        check(not winRow 0b0001101)
    let m: Mask = [
        {1, 2, 3},
        {0, 3},
        {1, 2},
        {0, 1},
        {0, 4},
        {3, 4, 5},
        {1, 2, 3},
    ]
    test "row":
        check(m.row(0) == 0b0011010)
        check(m.row(1) == 0b1001101)
    test "ascDiag":
        check(m.ascDiag(1, 1) == 0b110100)
    test "descDiag":
        check(m.descDiag(1, 1) == 0b100)
    test "winMove":
        let m: Mask = [{1, 2, 3, 4}, {}, {}, {}, {}, {}, {}]
        check(m.winMove(0, 4))
    test "top":
        check((ours: m, theirs: m).top(5) == none(Y))
        check((ours: m, theirs: m).top(1) == some(Y(4)))
]#

proc applyMove (board: var Board, x: X, ours: bool) =
    let y = board.top(x).get
    if ours:
        board.ours[x].incl y
    else:
        board.theirs[x].incl y

proc maximize (board: Board, depth: uint8): tuple[score: float, move: X] =
    if depth > 7:
        return
    result.score = -1
    var foundValidMove = false
    for x in X(0)..6:
        let top = board.top(x)
        if top.isNone:
            continue
        let y = top.unsafeGet
        foundValidMove = true
        # Pick a valid move, even if it may seem futile.
        if result.score == -1:
            result.move = x
        var ours = board.ours
        ours[x].incl y
        if ours.winMove(x, y):
            return (score: 1, move: x)
        let min = -(ours: board.theirs, theirs: ours).maximize(depth + 1).score
        # Indirect win forcable?
        if min == 1:
            return (score: 1, move: x)
        if min > result.score:
            result.score = min
            result.move = x
    # Returns an invalid move value (0) but meh
    if not foundValidMove:
        result.score = 0
        

var board: Board

proc makeMove () =
    let move = board.maximize(0).move
    board.applyMove(move, true)
    echo $(move + 1)

let line = stdin.readLine
if line == "f":
    makeMove()
else:
    assert line == "s"

while true:
    let move = stdin.readLine.parseUInt()
    assert move >= 1 and move <= 7
    board.applyMove(X(move - 1), false)
    makeMove()
client.nim 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
import os
import osproc
import std/strutils
import std/streams

type
    Spot = enum Free, Ours, Theirs
    Stack = array[6, Spot]
    Board = array[7, Stack]

var board: Board
var winner: Spot = Free

proc printBoard =
    for row in countdown(5, 0):
        for col in 0..6:
            const spotChar: array[Spot, char] = ['.', 'O', 'X']
            stdout.write spotChar[board[col][row]]
        stdout.write '\n'
    stdout.flushFile()

proc invalidMove (move: uint): bool =
    move < 1 or move > 7 or board[move - 1][^1] != Free

proc inBounds (x, y: int): bool =
    x >= 0 and x < 7 and y >= 0 and y < 6

proc countDir(x, y: uint, dx, dy: int): uint =
    var count: uint = 0
    var x = int(x)
    var y = int(y)
    let expected = board[x][y]
    while true:
        x += dx
        y += dy
        if not inBounds(x, y) or board[x][y] != expected:
            break
        count += 1
    return count

proc checkWinDir (x, y: uint, dx, dy: int): bool =
    return countDir(x, y, -dx, -dy) + 1 + countDir(x, y, dx, dy) >= 4

proc checkWin (x, y: uint): bool =
    checkWinDir(x, y, 0, 1) or checkWinDir(x, y, 1, 0) or checkWinDir(x, y, 1, 1)

var moves = 0
proc makeMove (move: uint, player: Spot) =
    assert player != Free
    for y, spot in board[move]:
        if spot == Free:
            board[move][y] = player
            if checkWin(move, uint(y)):
                winner = player
            moves += 1
            break

var bot = startProcess(paramStr(1), options = {})
var botIn = bot.inputStream
var botOut = bot.outputStream
proc sendCommand (command: string) =
    botIn.writeLine command
    botIn.flush
sendCommand "s" # hoomans go first!

while true:
    printBoard()
    if winner != Free or moves == 6*7:
        break
    block:
        echo "Make a move:"
        let move = stdin.readLine.parseUInt
        if invalidMove(move):
            echo "Invalid move"
            continue
        makeMove(move - 1, Ours)
        sendCommand $move
    if winner == Free:
        var line: string
        assert botOut.readLine line
        let move = line.parseUInt
        if invalidMove(move):
            echo "Invalid move from bot"
            break # can't resume with a broken bot
        makeMove(move - 1, Theirs)
bot.terminate
bot.close

const outcome: array[Spot, string] = ["Draw", "You win!", "They win!"]
echo outcome[winner]
help.txt ASCII text
1
2
Compile bot (or client): nim compile -d:release bot
Use client: ./client bot

round #44

submitted at
0 likes

guesses
comments 0

post a comment


countdown.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
#programmer time (MY TIME) is worth MOAR than cpu time!!!!!!
import argparse
from collections import Counter
from itertools import permutations,product
class Param:
	n=1
	def eval(it): return next(it)
class Expr:
	def __init__(self,l,op,r):
		self.l,self.op,self.r,self.n=l,op,r,l.n+r.n
	def eval(self,it):
		l,r=self.l.eval(it),self.r.eval(it)
		match self.op:
			case '+': return l+r
			case '*': return l*r
			case '-': return float('nan') if l<r else l-r
			case '/': return float('nan') if not r or l%r else l/r
class Bag:
	def __init__(self,s):
		self.ops=Counter()
		self.digits=Counter()
		for c in s:
			if c in '+-*/':
				self.ops.update((c,))
			elif (d:=ord(c)-ord('0')) in range(10):
				self.digits.update((d,))
			else:
				raise ValueError('invalid string')
	def _exprs(ops):
		yield Param
		if not any(ops.values()): return
		nz=[o for o in ops if ops[o]]
		if sum(1 for _ in nz)==1 and (op:=nz[0]) in '+*':
			e=Param
			for _ in range(ops[op]):
				yield (e:=Expr(e,op,Param))
			return
		for op in ops:
			ops[op]-=1
			for splt in product(*(range(ops[o]+1) for o in ops)):
				for l,r in product(Bag._exprs({o:n for n,o in zip(splt,ops)}),Bag._exprs({o:ops[o]-n for n,o in zip(splt,ops)})):
					yield Expr(l,op,r)
			ops[op]+=1
	def exprs(self): return Bag._exprs(self.ops)
	def params(self,n): return permutations(self.digits,r=n)
	def solve(self,num): return min((expr.eval(iter(params)) for expr in self.exprs() for params in self.params(expr.n)),key=lambda x:abs(num-x))
if __name__=='__main__':
	parser=argparse.ArgumentParser()
	parser.add_argument('bag')
	parser.add_argument('num')
	args=parser.parse_args()
	print(Bag(args.bag).solve(int(args.num)))
ink.jpg JPEG image data, JFIF standard 1.01, resolution (DPI), density 300x300, segment length 16, progressive, precision 8, 4160x3120, components 3

round #43

submitted at
0 likes

guesses
comments 0

post a comment


dir spirou
main.lua 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
-- (c) 2021 🥺🥺🥺
-- this code was manualy cursed to meet the lowest standards ✅✅✅
-- "Inter-token spacing is the root of all evil." - Mahatma Gandhi
local splurped={}
do
	local ts={_G}
	local s={}
	repeat
		local ns={}
		local fr={}
		for _,t in pairs(ts) do
			s[t]=1
			for k,v in pairs(t) do
				if fr[k]==nil then
					fr[k]=v
				else
					fr[k]=fr
				end
				if type(v)=="table" and not s[v] then
					ns[#ns+1]=v
				end
			end
		end
		for k,v in pairs(fr) do
			if splurped[k]==nil and v~=fr then
				splurped[k]=v
			end
		end
		ts=ns
	until#ts==0
end
setfenv(1,setmetatable({},{__index=splurped}))
local function mapva(f,...)
	if select("#",...)==0 then return end
	return f((...)),mapva(f,select(2,...))
end
p,x,pts,stp=400,0,{},{} -- 400² is the HOLY RESOLUTION - DO NOT TOUCH!
function love.load(args)
	R,r,d=mapva(tonumber,unpack(args))
	m=p/(R-r+abs(d))*0.5*0.75
	R,r,d=R*m,r*m,d*m
	mdt = min(abs(1/(R-r)),abs(r/(R-r)/d))/10
	setTitle(((r<0)and"epi"or"hypo").."trochoid")
	setMode(p,p)
end
function love.update(fdt)
	n=ceil(fdt/mdt)
	dt=fdt/n
	for i=1,n do
		x=x-dt
		y=-x*(R-r)/r
		cx,cy=p/2,p/2
		rx,ry=cx+(R-r)*cos(x),cy+(R-r)*sin(x)
		px,py=floor(rx-d*cos(y)),floor(ry-d*sin(y))
		h=py*p+px
		if not stp[h] then
			stp[h]=1
			insert(pts,{px,py})
		end
	end
end
function love.draw()
	setColor(0,1,0,1)
	ellipse("line",cx,cy,R,R)
	ellipse("line",rx,ry,abs(r),abs(r))
	line(rx,ry,px,py)
	setColor(1,0,0,1)
	points(pts)
end
uhh.txt ASCII text
1
2
3
so uh i maded some sussy hax but they shoud work on
LOVE 11.3 (Mysterious Mysteries)
oh uh also run is "love . R r d" in foldr

round #42

submitted at
0 likes

guesses
comments 0

post a comment


dir 42
dir src
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
 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
// Simple 2048 game for the terminal

use crossterm::{
    self, cursor,
    event::{read, Event, KeyCode},
    execute, queue, terminal,
};
use rand::Rng;
use std::{
    fmt,
    io::{self, Stdout, Write},
};

#[derive(Copy, Clone, PartialEq)]
enum Move {
    Left,
    Right,
    Up,
    Down,
}

impl TryFrom<KeyCode> for Move {
    type Error = ();
    fn try_from(value: KeyCode) -> Result<Self, Self::Error> {
        match value {
            KeyCode::Left | KeyCode::Char('a') => Ok(Move::Left),
            KeyCode::Right | KeyCode::Char('d') => Ok(Move::Right),
            KeyCode::Up | KeyCode::Char('w') => Ok(Move::Up),
            KeyCode::Down | KeyCode::Char('s') => Ok(Move::Down),
            _ => Err(()),
        }
    }
}

#[derive(PartialEq, Clone)]
struct Board {
    tiles: [[u8; 4]; 4],
}

impl Board {
    fn random_tile() -> u8 {
        if rand::thread_rng().gen_range(1..=10) == 10 {
            2
        } else {
            1
        }
    }
    fn place_random_tile(&mut self) {
        loop {
            let y = rand::thread_rng().gen_range(0..4);
            let x = rand::thread_rng().gen_range(0..4);
            if self.tiles[y][x] == 0 {
                self.tiles[y][x] = Self::random_tile();
                break;
            }
        }
    }
    fn init() -> Self {
        let mut this = Self { tiles: [[0; 4]; 4] };
        this.place_random_tile();
        this.place_random_tile();
        this
    }
    fn transpose(&mut self) {
        let tiles = &mut self.tiles;
        for y in 0..tiles.len() {
            for x in 0..y {
                (tiles[y][x], tiles[x][y]) = (tiles[x][y], tiles[y][x]);
            }
        }
    }
    fn reverse_rows(&mut self) {
        for row in self.tiles.as_mut() {
            row.reverse()
        }
    }
    fn move_left(&mut self) {
        for row in self.tiles.as_mut() {
            for x in 1..4 {
                let mut nx = x;
                while nx > 0 && row[nx - 1] == 0 {
                    nx -= 1;
                }
                if nx > 0 && row[nx - 1] == row[x] {
                    row[nx - 1] += 1;
                    row[x] = 0;
                } else if nx < x {
                    row[nx] = row[x];
                    row[x] = 0;
                }
            }
        }
    }
    fn make_move(&mut self, mov: Move) {
        match mov {
            Move::Left => self.move_left(),
            Move::Right => {
                self.reverse_rows();
                self.move_left();
                self.reverse_rows();
            }
            Move::Up => {
                self.transpose();
                self.move_left();
                self.transpose();
            }
            Move::Down => {
                self.transpose();
                self.reverse_rows();
                self.move_left();
                self.reverse_rows();
                self.transpose();
            }
        }
    }
    fn valid_moves(&self) -> impl Iterator<Item = &Move> {
        [Move::Left, Move::Right, Move::Up, Move::Down]
            .iter()
            .filter(|&&mov| {
                let mut clone = self.clone();
                clone.make_move(mov);
                clone != *self
            })
    }
    fn won(&self) -> bool {
        const TILE_2048: u8 = 11;
        self.tiles.iter().flatten().any(|&t| t == TILE_2048)
    }
}

// TODO (...) the hardcoded carriage returns for raw terminal mode feel dirty.
impl fmt::Display for Board {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let sep: String = "+----".repeat(4) + "+\r\n";
        for row in self.tiles {
            f.write_str(&sep)?;
            f.write_str(
                &(String::from("|")
                    + &row
                        .map(|tile| {
                            if tile == 0 {
                                "    ".into()
                            } else {
                                format!("{:^4}", 1 << tile)
                            }
                        })
                        .join("|")
                    + "|\r\n"),
            )?;
        }
        f.write_str(&sep)?;
        Ok(())
    }
}

struct RawTerminal {
    stdout: Stdout,
}
impl RawTerminal {
    fn init() -> Result<Self, io::Error> {
        terminal::enable_raw_mode()?;
        let mut stdout = io::stdout();
        execute!(
            stdout,
            terminal::EnterAlternateScreen,
            crossterm::cursor::Hide
        )?;
        Ok(Self { stdout })
    }
    fn queue_reset(&mut self) -> Result<(), io::Error> {
        queue!(
            self.stdout,
            terminal::Clear(terminal::ClearType::FromCursorUp),
            cursor::MoveTo(0, 0)
        )
    }
    fn flush(&mut self) -> Result<(), io::Error> {
        self.stdout.flush()
    }
}
impl Drop for RawTerminal {
    fn drop(&mut self) {
        execute!(
            self.stdout,
            crossterm::cursor::Show,
            terminal::LeaveAlternateScreen
        )
        .unwrap();
        terminal::disable_raw_mode().unwrap();
    }
}

enum GameOutcome {
    Win,
    Loss,
    Quit,
}

fn play() -> Result<GameOutcome, io::Error> {
    let mut terminal = RawTerminal::init()?;
    let mut board = Board::init();
    'game: loop {
        terminal.queue_reset()?;
        print!("{board}");
        print!("arrow keys or WASD to play, q to quit");
        terminal.flush()?;
        if board.won() {
            return Ok(GameOutcome::Win);
        }
        let valid_moves: Vec<&Move> = board.valid_moves().collect();
        if valid_moves.is_empty() {
            return Ok(GameOutcome::Loss);
        }
        let mov = loop {
            match read()? {
                Event::Key(event) => {
                    if event.code == KeyCode::Char('q') {
                        return Ok(GameOutcome::Quit);
                    }
                    if let Ok(mov) = Move::try_from(event.code) {
                        if valid_moves.contains(&&mov) {
                            break mov;
                        }
                    }
                }
                Event::Resize(_, _) => {
                    continue 'game;
                }
                _ => (),
            }
        };
        board.make_move(mov);
        board.place_random_tile();
    }
}

fn main() {
    let outcome = play().unwrap();
    println!(
        "{}",
        match outcome {
            GameOutcome::Win => "You win!",
            GameOutcome::Loss => "You lose!",
            GameOutcome::Quit => "quit",
        }
    );
}
Cargo.lock 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
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "_2048"
version = "0.1.0"
dependencies = [
 "crossterm",
 "rand",
]

[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"

[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"

[[package]]
name = "bitflags"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"

[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"

[[package]]
name = "crossterm"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
dependencies = [
 "bitflags 2.4.0",
 "crossterm_winapi",
 "libc",
 "mio",
 "parking_lot",
 "signal-hook",
 "signal-hook-mio",
 "winapi",
]

[[package]]
name = "crossterm_winapi"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
dependencies = [
 "winapi",
]

[[package]]
name = "getrandom"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
 "cfg-if",
 "libc",
 "wasi",
]

[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"

[[package]]
name = "lock_api"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
 "autocfg",
 "scopeguard",
]

[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"

[[package]]
name = "mio"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
dependencies = [
 "libc",
 "log",
 "wasi",
 "windows-sys",
]

[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
 "lock_api",
 "parking_lot_core",
]

[[package]]
name = "parking_lot_core"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
 "cfg-if",
 "libc",
 "redox_syscall",
 "smallvec",
 "windows-targets",
]

[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"

[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
 "libc",
 "rand_chacha",
 "rand_core",
]

[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
 "ppv-lite86",
 "rand_core",
]

[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
 "getrandom",
]

[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
 "bitflags 1.3.2",
]

[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"

[[package]]
name = "signal-hook"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
dependencies = [
 "libc",
 "signal-hook-registry",
]

[[package]]
name = "signal-hook-mio"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
dependencies = [
 "libc",
 "mio",
 "signal-hook",
]

[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
 "libc",
]

[[package]]
name = "smallvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"

[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"

[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
 "winapi-i686-pc-windows-gnu",
 "winapi-x86_64-pc-windows-gnu",
]

[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
 "windows-targets",
]

[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
 "windows_aarch64_gnullvm",
 "windows_aarch64_msvc",
 "windows_i686_gnu",
 "windows_i686_msvc",
 "windows_x86_64_gnu",
 "windows_x86_64_gnullvm",
 "windows_x86_64_msvc",
]

[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"

[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"

[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"

[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"

[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"

[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"

[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
Cargo.toml ASCII text
1
2
3
4
5
6
7
8
[package]
name = "_2048"
version = "0.1.0"
edition = "2021"

[dependencies]
rand = "0.8.5"
crossterm = "0.27.0"

round #41

submitted at
4 likes

guesses
comments 0

post a comment


41.zig 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
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
435
436
437
438
439
440
441
442
443
444
445
446
447
448
// FALSE bytecode compiler & VM for round 41 of code guessing (https://cg.esolangs.gay/41/).
// Written by Lars Müller. Licensed under the MIT license.
// Compile using `zig build-exe 41.zig -O ReleaseFast -fstrip -fsingle-threaded`.

const std = @import("std");

const Allocator = std.mem.Allocator;

const Op = enum(u32) {
	// 🥺
	Push,
	GetReg,
	SetReg,
	// Stack reordering
	Dup,
	Drop,
	Swap,
	Rot,
	Index,
	// Binops
	Add,
	Sub,
	Mul,
	Div,
	And,
	Or,
	Eq,
	Gt,
	// Unops
	Inv,
	Neg,
	// Control flow
	Jmp,
	Call,
	CallIf,
	Ret,
	LoopSetup,
	LoopTest,
	LoopBody,
	// I/O
	ReadByte,
	WriteByte,
	WriteInt, // technically redundant (like plenty of other instructions too) but I'm too laziggy and performance or something
	Flush,
};

// teknikhally this is a bytecode builder - 🤓
const Bytecode = struct {
	allocator: Allocator,
	words: std.ArrayList(u32),
	pub fn init(allocator: Allocator) Bytecode {
		return Bytecode{.allocator = allocator, .words = std.ArrayList(u32).init(allocator)};
	}
	pub fn deinit(self: *Bytecode) void {
		self.words.deinit();
	}
	pub fn getWords(self: *Bytecode) []u32 {
		return self.words.items;
	}
	fn emit(self: *Bytecode, op: Op) !void {
		try self.words.append(@intFromEnum(op));
	}
	fn emitToPatch(self: *Bytecode) !usize {
		try self.words.append(0xDEADBEEF);
		return self.words.items.len - 1;
	}
	fn patch(self: *Bytecode, i: usize, u: u32) void {
		self.words.items[i] = u;
	}
	fn emitPush(self: *Bytecode, u: u32) !void {
		try self.emit(.Push);
		try self.words.append(u);
	}
	fn append(self: *Bytecode, other: Bytecode) !u32 {
		const nextPos: u32 = @intCast(self.words.items.len);
		try self.words.appendSlice(other.words.items);
		return nextPos;
	}
};

// shot🔫 parser & bytecode emitter
fn Compiler(comptime R: type) type {
	return struct {
		const Self = @This();
		allocator: Allocator,
		in: R,
		b: ?u8 = null,
		bytecode: Bytecode,
		pub fn init(allocator: Allocator, in: R) Self {
			return Self{.allocator = allocator, .in = in, .bytecode = Bytecode.init(allocator)};
		}
		pub fn reset(self: *Self) void {
			self.b = null;
			self.bytecode = Bytecode.init(self.allocator);
		}
		pub fn deinit(self: *Self) void {
			self.bytecode.deinit();
		}

		fn readByte(self: *Self) !u8 {
			const b = self.b;
			if (b == null)
				return self.in.readByte();
			self.b = null;
			return b.?;
		}
		fn unreadByte(self: *Self, b: u8) void {
			self.b = b;
		}

		pub fn compileFn(self: *Self) !u32 {
			var bytecode = Bytecode.init(self.allocator);
			defer bytecode.deinit();
			while (true) {
				const b = self.readByte() catch |err| {
					if (err == error.EndOfStream) break;
					return err;
				};
				try switch (b) {
					'\t', ' ', '\n' => {},
					'{' => while (try self.readByte() != '}') {},
					'"' => while (true) {
						const quotedB = try self.readByte();
						if (quotedB == '"') break;
						try bytecode.emitPush(@as(u32, quotedB));
						try bytecode.emit(.WriteByte);
					},
					'\'' => {
						const quotedB = try self.readByte();
						try bytecode.emitPush(@as(u32, quotedB));
					},
					'0' ... '9' => {
						var u: u32 = b - '0';
						var nb: u8 = undefined;
						while (true) {
							nb = self.readByte() catch |err| {
								if (err == error.EndOfStream) break;
								return err;
							};
							if (nb < '0' or nb > '9') break;
							u = 10 * u + nb - '0';
						}
						self.unreadByte(nb);
						try bytecode.emitPush(u);
					},
					'a' ... 'z' => try bytecode.emitPush(@as(u32, b - 'a')),
					'[' => {
						try bytecode.emit(.Push);
						const i = try bytecode.emitToPatch();
						bytecode.patch(i, try self.compileFn());
						if (try self.readByte() != ']') return error.UnclosedQuote;
					},
					']' => {
						self.unreadByte(']');
						break;
					},
					'!' => bytecode.emit(.Call),
					'?' => bytecode.emit(.CallIf),
					'#' => {
						// too laziggy to relocate addresses for jumps so here you go, loops get three instructions
						for ([_]Op{.LoopSetup, .LoopTest, .LoopBody}) |op| try bytecode.emit(op);
					},
					else => try bytecode.emit(switch (b) {
						';' => .GetReg,
						':' => .SetReg,
						'$' => .Dup,
						'%' => .Drop,
						'\\' => .Swap,
						'@' => .Rot,
						'O' => .Index,
						'+' => .Add,
						'-' => .Sub,
						'/' => .Div,
						'*' => .Mul,
						'|' => .Or,
						'&' => .And,
						'=' => .Eq,
						'>' => .Gt,
						'~' => .Inv,
						'_' => .Neg,
						'^' => .ReadByte,
						',' => .WriteByte,
						'.' => .WriteInt,
						'B' => .Flush,
						else => return error.InvalidInstruction, // includes '`'
					}),
				};
			}
			try bytecode.emit(.Ret);
			return try self.bytecode.append(bytecode);
		}
		pub fn compile(self: *Self) !Bytecode {
			try self.bytecode.emit(.Push);
			const i = try self.bytecode.emitToPatch();
			try self.bytecode.emit(.Jmp);
			self.bytecode.patch(i, try self.compileFn());
			const bytecode = self.bytecode;
			self.reset();
			return bytecode;
		}
	};
}

// we skimp on error checking here (gotta go fast);
// FALSE programmers can be trusted to not ever make oopsies
// thus there is no typechecking (num/quote/char/reg etc.) here,
// no bounds checking, no stack overflow or underflow checking
// (Zig will trap the "unreachable" code though)
fn VM(comptime R: type, comptime W: type) type {
	return struct {
		const Self = @This();
		stack: std.ArrayList(u32),
		retstack: std.ArrayList(u32),
		regs: [32]u32 = undefined, // did you want zeroes? no zeroes for you.
		// i already gifted you a few registers, be grateful!
		in: R,
		out: W,
		pub fn init(allocator: Allocator, in: R, out: W) Self {
			return Self{
				.stack = std.ArrayList(u32).init(allocator),
				.retstack = std.ArrayList(u32).init(allocator),
				.in = in,
				.out = out,
			};
		}
		pub fn deinit(vm: *Self) void {
			vm.flush();
			vm.stack.deinit();
			vm.retstack.deinit();
		}
		fn pop(vm: *Self) u32 {
			return vm.stack.pop(); // what is an error?
		}
		fn push(vm: *Self, u: u32) void {
			vm.stack.append(u) catch unreachable; // gotta go fast
		}
		fn drop(vm: *Self) void {
			_ = vm.pop();
		}
		fn dup(vm: *Self) void {
			vm.push(vm.stack.getLast());
		}
		fn swap(vm: *Self) void {
			const top = vm.pop();
			const bot = vm.pop();
			vm.push(top);
			vm.push(bot);
		}
		fn rot(vm: *Self) void {
			const top = vm.pop();
			const mid = vm.pop();
			const bot = vm.pop();
			vm.push(mid);
			vm.push(top);
			vm.push(bot);
		}
		fn index(vm: *Self) void {
			const j = vm.pop();
			const i = vm.stack.items.len - j - 1;
			vm.push(vm.stack.items[i]); // what is an OOB
		}

		fn binop(vm: *Self, comptime op: fn(lhs: u32, rhs: u32) u32) void {
			const rhs = vm.pop();
			const lhs = vm.pop();
			vm.push(op(lhs, rhs));
		}
		// u32, i32, it's all the same (modular arithmetic says hi)
		fn add(lhs: u32, rhs: u32) u32 { return lhs +% rhs; }
		fn sub(lhs: u32, rhs: u32) u32 { return lhs -% rhs; }
		fn mul(lhs: u32, rhs: u32) u32 { return lhs *% rhs; }
		fn div(lhs: u32, rhs: u32) u32 { return @divTrunc(lhs, rhs); }
		fn band(lhs: u32, rhs: u32) u32 { return lhs & rhs; }
		fn bor(lhs: u32, rhs: u32) u32 { return lhs | rhs; }
		fn eq(lhs: u32, rhs: u32) u32 { return if (lhs == rhs) ~@as(u32, 0) else 0; }
		// okay *maybe* i lied and it's not quite as shrimple
		fn gt(lhs: u32, rhs: u32) u32 {
			// *portability* rules so let's write some inefficient branching code
			const sl = lhs >> 31;
			const sr = rhs >> 31;
			return if (if (sl == sr) lhs > rhs else sl < sr) ~@as(u32, 0) else 0;
		}

		fn unop(vm: *Self, comptime op: fn(i: u32) u32) void { vm.push(op(vm.pop())); }
		fn negate(i: u32) u32 { return 1 +% ~i; } // you should have recognized this!
		fn invert(i: u32) u32 { return ~i; }

		fn getReg(vm: *Self) void {
			const i = vm.pop();
			vm.push(vm.regs[@intCast(i)]); // OOBs don't happen, do they?
		}
		fn setReg(vm: *Self) void {
			const i = vm.pop();
			const v = vm.pop();
			vm.regs[@intCast(i)] = v;
		}

		fn readByte(vm: *Self) void {
			// no distinguishing EOF and other "errors" because yes
			vm.push(vm.in.readByte() catch ~@as(u32, 0));
		}
		fn _writeByte(vm: *Self, b: u8) void {
			const bs = [_]u8{b};
			// should we assert that it wrote exactly one character?
			_ = vm.out.write(bs[0..]) catch unreachable; // nah, nothing will go wrong.
		}
		fn writeByte(vm: *Self) void {
			vm._writeByte(@truncate(vm.pop()));
		}
		fn writeInt(vm: *Self) void {
			var i = vm.pop();
			const negative = i >> 31 == 1;
			if (negative) {
				vm._writeByte('-');
				i = 1 + ~i;
			}
			// maybe i should look up the library function for this
			var buf: [16]u8 = undefined;
			var j: u8 = 0;
			while (true) {
				buf[j] = @truncate(i % 10);
				j += 1;
				i = @divTrunc(i, 10);
				if (i == 0) break;
			}
			while (true) {
				j -= 1;
				vm._writeByte(buf[j] + '0');
				if (j == 0) break;
			}
		}
		fn flush(vm: *Self) void {
			vm.out.flush() catch unreachable;
		}
		
		pub fn run(vm: *Self, bytecode: []const u32) void {
			vm.retstack.append(@intCast(bytecode.len)) catch unreachable; // main func returning should end the program not pop an empty stack
			var ip: u32 = 0;
			while (ip < bytecode.len) {
				const op: Op = @enumFromInt(bytecode[ip]);
				switch (op) {
					// Control flow
					.Push => {
						vm.push(bytecode[ip + 1]); // surely this won't OOB
						ip += 2;
						continue;
					},
					.Jmp => {
						ip = vm.pop();
						continue;
					},
					.Call => {
						vm.retstack.append(ip + 1) catch unreachable;
						ip = vm.pop();
						continue;
					},
					.Ret => {
						ip = vm.retstack.pop();
						continue;
					},
					.CallIf => {
						const q = vm.pop();
						const v = vm.pop();
						if (v != 0) {
							vm.retstack.append(ip + 1) catch unreachable; // gotta go fast
							ip = q;
							continue;
						}
					},
					.LoopSetup => {
						const body = vm.pop();
						const cond = vm.pop();
						vm.retstack.append(body) catch unreachable;
						vm.retstack.append(cond) catch unreachable;
					},
					.LoopTest => {
						const cond = vm.retstack.getLast();
						vm.retstack.append(ip + 1) catch unreachable; // gotta go fast
						ip = cond;
						continue;
					},
					.LoopBody => {
						if (vm.pop() != 0) {
							const body = vm.retstack.items[vm.retstack.items.len - 2];
							vm.retstack.append(ip - 1) catch unreachable; // this is a sneaky one
							ip = body;
							continue;
						}
						// pop cond & body
						_ = vm.retstack.pop();
						_ = vm.retstack.pop();
					},
					// everything else
					.Dup => vm.dup(),
					.Drop => vm.drop(),
					.Swap => vm.swap(),
					.Rot => vm.rot(),
					.Index => vm.index(),
					.Add => vm.binop(add),
					.Sub => vm.binop(sub),
					.Mul => vm.binop(mul),
					.Div => vm.binop(div),
					.And => vm.binop(band),
					.Or => vm.binop(bor),
					.Eq => vm.binop(eq),
					.Gt => vm.binop(gt),
					.Neg => vm.unop(negate),
					.Inv => vm.unop(invert),
					.GetReg => vm.getReg(),
					.SetReg => vm.setReg(),
					.ReadByte => vm.readByte(),
					.WriteByte => vm.writeByte(),
					.WriteInt => vm.writeInt(),
					.Flush => vm.flush(),
				}
				ip += 1;
			}
		}
	};
}

pub fn main() !void {
	var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
	defer std.debug.assert(general_purpose_allocator.deinit() == .ok);
	const gpa = general_purpose_allocator.allocator();

	var args = try std.process.argsWithAllocator(gpa);
	defer args.deinit();
	if (!args.skip()) return error.InvalidUsage;
	const path = args.next();
	if (path == null) return error.InvalidUsage;
	var file = try std.fs.cwd().openFile(path.?, .{});
	defer file.close();
	var buf = std.io.bufferedReader(file.reader());
	var in = buf.reader();

	var compiler = Compiler(@TypeOf(in)).init(gpa, in);
	defer compiler.deinit();
	var bytecode = try compiler.compile();
	defer bytecode.deinit();

	var bufStdin = std.io.bufferedReader(std.io.getStdIn().reader());
	var stdinReader = bufStdin.reader();
	var bufStdout = std.io.bufferedWriter(std.io.getStdOut().writer());
	var vm = VM(@TypeOf(stdinReader), @TypeOf(bufStdout)).init(gpa, stdinReader, bufStdout);
	defer vm.deinit();
	vm.run(bytecode.getWords());
}
untrue.false 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
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
{
	FALSE self-interpreter.

	Takes a FALSE program as input, followed by `<` as a delimiter, followed by program input.
	If there is no program input, the `<` delimiter can be omitted.
	Expects ASCII: B rather than ß and O rather than ø.

	Written by Lars Müller (https://github.com/appgurueu)
	for round 41 of code guessing (https://cg.esolangs.gay/41/).
	Licensed under the MIT license.
	

	Heavily uses variables as to not interfere with what it's doing on the stack
	(splicing the stack, emitting bytecode on the stack or using the stack as the interpreter stack).

	Some FALSE operations will have linear time complexity in the size of the stack in this interpreter.
	That is, polynomial vs. superpolynomial time is preserved, but programs may get a linear time complexity factor.
}

{
	shove/splice (s):
	pop n elements,
	then call f to mutate the stack,
	then restore the popped elements

	invocation: [...]f: 42n: 32m: s;!
}
[
	n;0=
	$[% f;! 0~]?
	~[
		m;0=
		$[
			% {kill cond}
			% {kill a zero on stack}
			n; 1- n:
			32 m:
			s;!
			0 {this is to be left on the stack}
			0~ {this will be killed (cond)}
		]?
		~[
			m; 1- m:
			$ 1& {extract lowest bit}
			\ 2/ \ {>> 1 thing on stack}
			$[
				% s;! 2* 1| 0~
			]?
			~[
				s;! 2*
			]?
		]?
	]?
]s:

{
	Compiler.
	Leaves bytecode on stack.
	Uses`<` to delimit program and input.
}

0l: {bytecode length}
0t: 0n: {state t: 0 = default, 1 = number; n holds the numerical value of the number}
[^c: c;'<= c;1_= | ~] {loop until `<` or EOF is encountered}
[
	1h: {to handle? set to 0 if handled}
	'0c;> c;'9> | ~[
		c; '0- n; 10* + n: {add digit to numerical value}
		1t: {we have a number now}
		0h:
	]?
	h;[
		t;['P n; l;2+l:]? {emit a num push if necessary}
		0t: 0n: {reset state to "not a number, numerical value 0"}
	]?
	{variables}
	'ac;> c;'z> | ~[
		'P c;'a-
		l;2+l:
		0h:
	]?
	{character literals}
	c;''=[
		'P ^ {pls don't EOF here oki?}
		l;2+l:
		0h:	
	]?
	{whitespace}
	c;32{space}= c;9{tab}= c;10{newline}= ||[
		0h: {ignore whitespace; don't push it}	
	]?
	{comments}
	c;'{=[
		[^'}=~][]#
		0h:
	]?
	{string literals}
	c;'"=[
		[^$'"=~][
			{push char, then write char}
			'P \ ',
			l;3+l:
		]#
		% {murder the closing "}
		0h:
	]?
	{lambdas}
	c;'[=[
		'J 55555 {jump, address is to be replaced}
		l;2+l:
		{NOTE: we can reuse n here since it was already emitted}
		[l;]f: l;n: 32m: s;! {push start address of function on stack}
		0h:
	]?
	c;']=[
		{TODO (...) check balance}
		[q:]f: l;n: 32m: s;! {this should pop the top of the funcinfo stack, which starts right under the bytecode}
		'R l;1+l: {emit return}
		l;q; - n: {this is the length of the function - how many things to skip until we can edit the jump address before the function}
		[% l;]f: 32m: s;! {replace jump address}
		'P q; {return, push lambda}
		l;2+l:
		0h:
	]?
	c;'#=[
		'S 'T 'L
		l;3+l:
		0h:
	]?
	{TODO (...) reject invalid characters}
	h;[
		c;
		l;1+l:
	]?
]#

t;['P n; l;2+l:]? {deal with a trailing number (not that it would matter)}

'E {mark end of bytecode}

{
	Interpreter.
	Does everything on the stack.
	Stack layout: Bytecode, call stack, variables, working stack.
}

l; b: {base of the stack (start of bytecode) relative to the top}
l; c: {top of the call stack, relative to base}
{variables come after the call stack (c+x); push 26 var slots}
0 i:
[i;26=~][
	123456 {for easier debugging}
	i;1+i:
]#
b;26+b:
0 i: {program counter relative to b}
[b;i;-O 'E =~] {loop until sentinel is reached}
[
	b;i;-O {fetch instruction}
	$'P=[% i;1+i: b;i;-O b;1+b: 'E]? {push 'P <value>}
	{variables}
	$';=[%
		{take something from the stack as offset to add to c, then use as index}
		b;c;-\- 2-{1- for what we just popped, another 1- because it is in ':} O
		{-1+1=0}
	'E]?
	$':=[%
		{stack: val regno}
		\ v: {save val}
		b;2-b:
		b;c;- \ - 1- n: {n = b - c - regno - 1}
		[% v;]f: 32m: s;! {replace variable on stack}
	'E]?
	{control flow}
	{jump 'J <address>}
	$'J=[%
		i;1+i:
		b;i;-O
		1-{to undo later 1+}i: {set PC}
	'E]?
	{pop and call}
	$'!=[%
		g: {pop & remember function to call}
		[i;1+]f: b;c;- n: 32m: s;! {push next pc to call stack}
		c;1+c:
		g;1-{to undo later 1+}i: {set PC}
		{-1+1=0}
	'E]?
	{conditional call}
	$'?=[%
		b;2-b: {will pop two things}
		g: {remember func addr.}
		[
			{push to call stack}
			[i;1+]f: b;1+{pretend it was one larger (cmp with '! to see why)}c;- n: 32m: s;!
			c;1+c:
			g;1-{to undo later 1+}i: {set pc}
			b;1+b: {pushed one thing on call stack}
		]? {this pops!}
	'E]?
	{loop setup}
	$'S=[%
		g: o: {remember func addr.}
		{push condition & body lambdas on the call stack}
		[g;o;]f: b;2-c;- n:
		32m: s;!
		c;2+c:
		{no need to adjust base; all we did was move two things from stack to call stack}
	'E]?
	{loop test}
	$'T=[%
		b;c;- O
		{this is just the code for a call of `g`}
		g:
		[i;1+]f: b;1+c;- n: 32m: s;!
		c;1+c:
		g;1-i:
		b;1+b: {pushed addr to call stack, popped nothing (cond remains on call stack)}
	'E]?
	{loop looping}
	$'L=[%
		b;1-b: w:
		w;[
			b;c;-1+{body lies one deeper than cond} O
			{this is just the code for a call of `g`}
			g:
			[i;1-{sneaky one: go back to T instr}]f: b;1+c;- n: 32m: s;!
			c;1+c:
			g;1-i:
			b;1+b:
		]?
		w;0=[
			{clean up call stack: drop cond and body}
			[%%]f: b;c;- n: 32m: s;!
			c;2-c:
			b;2-b:
		]?
	'E]?
	{return}
	$'R=[%
		c;1-c:
		{pop from call stack}
		[
			1-{to undo later 1+}i:
		]f: b;c;-n: 32m: s;!
		b;1-b:
	'E]?
	{binops}
	$'+=[% + b;1-b: 'E]?
	$'-=[% - b;1-b: 'E]?
	$'*=[% * b;1-b: 'E]?
	$'/=[% / b;1-b: 'E]?
	$'&=[% & b;1-b: 'E]?
	$'|=[% | b;1-b: 'E]?
	$'>=[% > b;1-b: 'E]?
	$'==[% = b;1-b: 'E]?
	{unops}
	$'~=[% ~ 'E]?
	$'_=[% _ 'E]?
	{stack ops; these are funky}
	$'\=[% \ 'E]?
	$'@=[% @ 'E]?
	$'%=[% % b;1-b: 'E]?
	$'$=[% $ b;1+b: 'E]?
	$'O=[% O {+1-1} 'E]?
	{I/O; compiler needs to handle "..."}
	$'B=[% B 'E]?
	$'^=[% ^ b;1+b: 'E]?
	$',=[% , b;1-b: 'E]?
	'.=[. B b;1-b:]?
	i;1+i:
]#

round #40

submitted at
1 like

guesses
comments 0

post a comment


15.zip Zip archive data, at least v2.0 to extract, compression method=deflate
dir 15
dir android
dir app
dir src
dir debug
AndroidManifest.xml ASCII text
1
2
3
4
5
6
7
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- The INTERNET permission is required for development. Specifically,
         the Flutter tool needs it to communicate with the running application
         to allow setting breakpoints, to provide hot reload, etc.
    -->
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>
dir main
dir kotlin
dir com
dir example
dir fifteen
MainActivity.kt ASCII text
1
2
3
4
5
6
package com.example.fifteen

import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity() {
}
dir res
dir drawable
launch_background.xml ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@android:color/white" />

    <!-- You can insert your own image assets here -->
    <!-- <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/launch_image" />
    </item> -->
</layer-list>
dir drawable-v21
launch_background.xml ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="?android:colorBackground" />

    <!-- You can insert your own image assets here -->
    <!-- <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/launch_image" />
    </item> -->
</layer-list>
dir mipmap-hdpi
ic_launcher.png PNG image data, 72 x 72, 8-bit colormap, non-interlaced
launcher_icon.png PNG image data, 72 x 72, 8-bit/color RGBA, non-interlaced
dir mipmap-mdpi
ic_launcher.png PNG image data, 48 x 48, 8-bit colormap, non-interlaced
launcher_icon.png PNG image data, 48 x 48, 8-bit/color RGBA, non-interlaced
dir mipmap-xhdpi
ic_launcher.png PNG image data, 96 x 96, 8-bit colormap, non-interlaced
launcher_icon.png PNG image data, 96 x 96, 8-bit/color RGBA, non-interlaced
dir mipmap-xxhdpi
ic_launcher.png PNG image data, 144 x 144, 8-bit colormap, non-interlaced
launcher_icon.png PNG image data, 144 x 144, 8-bit/color RGBA, non-interlaced
dir mipmap-xxxhdpi
ic_launcher.png PNG image data, 192 x 192, 8-bit colormap, non-interlaced
launcher_icon.png PNG image data, 192 x 192, 8-bit/color RGBA, non-interlaced
dir values
styles.xml ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
    <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
        <!-- Show a splash screen on the activity. Automatically removed when
             the Flutter engine draws its first frame -->
        <item name="android:windowBackground">@drawable/launch_background</item>
    </style>
    <!-- Theme applied to the Android Window as soon as the process has started.
         This theme determines the color of the Android Window while your
         Flutter UI initializes, as well as behind your Flutter UI while its
         running.

         This Theme is only used starting with V2 of Flutter's Android embedding. -->
    <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
        <item name="android:windowBackground">?android:colorBackground</item>
    </style>
</resources>
dir values-night
styles.xml ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
        <!-- Show a splash screen on the activity. Automatically removed when
             the Flutter engine draws its first frame -->
        <item name="android:windowBackground">@drawable/launch_background</item>
    </style>
    <!-- Theme applied to the Android Window as soon as the process has started.
         This theme determines the color of the Android Window while your
         Flutter UI initializes, as well as behind your Flutter UI while its
         running.

         This Theme is only used starting with V2 of Flutter's Android embedding. -->
    <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
        <item name="android:windowBackground">?android:colorBackground</item>
    </style>
</resources>
AndroidManifest.xml 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
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application
        android:label="15"
        android:name="${applicationName}"
        android:icon="@mipmap/launcher_icon">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
	<queries>
		<intent>
			<action android:name="android.intent.action.VIEW" />
			<category android:name="android.intent.category.BROWSABLE" />
			<data android:scheme="https" />
		</intent>
	</queries>
</manifest>
dir profile
AndroidManifest.xml ASCII text
1
2
3
4
5
6
7
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- The INTERNET permission is required for development. Specifically,
         the Flutter tool needs it to communicate with the running application
         to allow setting breakpoints, to provide hot reload, etc.
    -->
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>
build.gradle 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
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
    namespace "com.example.fifteen"
    compileSdkVersion flutter.compileSdkVersion
    ndkVersion flutter.ndkVersion

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.example.fifteen"
        // You can update the following values to match your application needs.
        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
        minSdkVersion flutter.minSdkVersion
        targetSdkVersion flutter.targetSdkVersion
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

    buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
}

flutter {
    source '../..'
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
dir gradle
dir wrapper
gradle-wrapper.properties ASCII text
1
2
3
4
5
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
build.gradle 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
buildscript {
    ext.kotlin_version = '1.7.10'
    repositories {
        google()
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:7.3.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.buildDir = '../build'
subprojects {
    project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
    project.evaluationDependsOn(':app')
}

tasks.register("clean", Delete) {
    delete rootProject.buildDir
}
gradle.properties ASCII text
1
2
3
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
settings.gradle ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
include ':app'

def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()

assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }

def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
dir assets
icon.png PNG image data, 128 x 128, 8-bit/color RGBA, non-interlaced
icon.svg SVG Scalable Vector Graphics image
 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
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   width="16"
   height="16"
   viewBox="0 0 4.2333332 4.2333333"
   version="1.1"
   id="svg5"
   inkscape:version="1.2.2 (732a01da63, 2022-12-09, custom)"
   sodipodi:docname="icon.svg"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg">
  <sodipodi:namedview
     id="namedview7"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:showpageshadow="2"
     inkscape:pageopacity="0.0"
     inkscape:pagecheckerboard="0"
     inkscape:deskcolor="#d1d1d1"
     inkscape:document-units="px"
     showgrid="false"
     inkscape:zoom="1.4039843"
     inkscape:cx="14.601303"
     inkscape:cy="8.1909749"
     inkscape:window-width="1850"
     inkscape:window-height="1016"
     inkscape:window-x="0"
     inkscape:window-y="0"
     inkscape:window-maximized="1"
     inkscape:current-layer="layer1" />
  <defs
     id="defs2" />
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1">
    <rect
       style="fill:#ffffff;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
       id="rect2269"
       width="3.96875"
       height="3.96875"
       x="0.14086828"
       y="0.14379625"
       ry="0.46099386" />
    <path
       style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 2.1166666,0.13229166 V 4.1010416"
       id="path3328"
       sodipodi:nodetypes="cc" />
    <path
       style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 4.1010417,2.1166665 H 0.13229166"
       id="path3328-3"
       sodipodi:nodetypes="cc" />
    <text
       xml:space="preserve"
       style="font-size:1.41111px;line-height:1.25;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
       x="0.85640651"
       y="1.7070621"
       id="text3507"><tspan
         sodipodi:role="line"
         id="tspan3505"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Comfortaa;-inkscape-font-specification:'Comfortaa Bold';stroke-width:0.264583"
         x="0.85640651"
         y="1.7070621">1</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:1.41111px;line-height:1.25;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
       x="2.7050855"
       y="1.7001066"
       id="text3507-6"><tspan
         sodipodi:role="line"
         id="tspan3505-7"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Comfortaa;-inkscape-font-specification:'Comfortaa Bold';stroke-width:0.264583"
         x="2.7050855"
         y="1.7001066">2</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:1.41111px;line-height:1.25;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
       x="0.72468543"
       y="3.7001522"
       id="text3507-6-5"><tspan
         sodipodi:role="line"
         id="tspan3505-7-3"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Comfortaa;-inkscape-font-specification:'Comfortaa Bold';stroke-width:0.264583"
         x="0.72468543"
         y="3.7001522">3</tspan></text>
  </g>
</svg>
dir ios
dir Flutter
AppFrameworkInfo.plist 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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleDevelopmentRegion</key>
  <string>en</string>
  <key>CFBundleExecutable</key>
  <string>App</string>
  <key>CFBundleIdentifier</key>
  <string>io.flutter.flutter.app</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>6.0</string>
  <key>CFBundleName</key>
  <string>App</string>
  <key>CFBundlePackageType</key>
  <string>FMWK</string>
  <key>CFBundleShortVersionString</key>
  <string>1.0</string>
  <key>CFBundleSignature</key>
  <string>????</string>
  <key>CFBundleVersion</key>
  <string>1.0</string>
  <key>MinimumOSVersion</key>
  <string>11.0</string>
</dict>
</plist>
Debug.xcconfig ASCII text
1
#include "Generated.xcconfig"
Release.xcconfig ASCII text
1
#include "Generated.xcconfig"
dir Runner
dir Assets.xcassets
dir AppIcon.appiconset
Contents.json JSON text data
  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
{
  "images" : [
    {
      "size" : "20x20",
      "idiom" : "iphone",
      "filename" : "Icon-App-20x20@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "20x20",
      "idiom" : "iphone",
      "filename" : "Icon-App-20x20@3x.png",
      "scale" : "3x"
    },
    {
      "size" : "29x29",
      "idiom" : "iphone",
      "filename" : "Icon-App-29x29@1x.png",
      "scale" : "1x"
    },
    {
      "size" : "29x29",
      "idiom" : "iphone",
      "filename" : "Icon-App-29x29@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "29x29",
      "idiom" : "iphone",
      "filename" : "Icon-App-29x29@3x.png",
      "scale" : "3x"
    },
    {
      "size" : "40x40",
      "idiom" : "iphone",
      "filename" : "Icon-App-40x40@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "40x40",
      "idiom" : "iphone",
      "filename" : "Icon-App-40x40@3x.png",
      "scale" : "3x"
    },
    {
      "size" : "60x60",
      "idiom" : "iphone",
      "filename" : "Icon-App-60x60@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "60x60",
      "idiom" : "iphone",
      "filename" : "Icon-App-60x60@3x.png",
      "scale" : "3x"
    },
    {
      "size" : "20x20",
      "idiom" : "ipad",
      "filename" : "Icon-App-20x20@1x.png",
      "scale" : "1x"
    },
    {
      "size" : "20x20",
      "idiom" : "ipad",
      "filename" : "Icon-App-20x20@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "29x29",
      "idiom" : "ipad",
      "filename" : "Icon-App-29x29@1x.png",
      "scale" : "1x"
    },
    {
      "size" : "29x29",
      "idiom" : "ipad",
      "filename" : "Icon-App-29x29@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "40x40",
      "idiom" : "ipad",
      "filename" : "Icon-App-40x40@1x.png",
      "scale" : "1x"
    },
    {
      "size" : "40x40",
      "idiom" : "ipad",
      "filename" : "Icon-App-40x40@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "76x76",
      "idiom" : "ipad",
      "filename" : "Icon-App-76x76@1x.png",
      "scale" : "1x"
    },
    {
      "size" : "76x76",
      "idiom" : "ipad",
      "filename" : "Icon-App-76x76@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "83.5x83.5",
      "idiom" : "ipad",
      "filename" : "Icon-App-83.5x83.5@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "1024x1024",
      "idiom" : "ios-marketing",
      "filename" : "Icon-App-1024x1024@1x.png",
      "scale" : "1x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}
Icon-App-1024x1024@1x.png PNG image data, 1024 x 1024, 8-bit/color RGBA, non-interlaced
Icon-App-20x20@1x.png PNG image data, 20 x 20, 8-bit/color RGBA, non-interlaced
Icon-App-20x20@2x.png PNG image data, 40 x 40, 8-bit/color RGBA, non-interlaced
Icon-App-20x20@3x.png PNG image data, 60 x 60, 8-bit/color RGBA, non-interlaced
Icon-App-29x29@1x.png PNG image data, 29 x 29, 8-bit/color RGBA, non-interlaced
Icon-App-29x29@2x.png PNG image data, 58 x 58, 8-bit/color RGBA, non-interlaced
Icon-App-29x29@3x.png PNG image data, 87 x 87, 8-bit/color RGBA, non-interlaced
Icon-App-40x40@1x.png PNG image data, 40 x 40, 8-bit/color RGBA, non-interlaced
Icon-App-40x40@2x.png PNG image data, 80 x 80, 8-bit/color RGBA, non-interlaced
Icon-App-40x40@3x.png PNG image data, 120 x 120, 8-bit/color RGBA, non-interlaced
Icon-App-50x50@1x.png PNG image data, 50 x 50, 8-bit/color RGBA, non-interlaced
Icon-App-50x50@2x.png PNG image data, 100 x 100, 8-bit/color RGBA, non-interlaced
Icon-App-57x57@1x.png PNG image data, 57 x 57, 8-bit/color RGBA, non-interlaced
Icon-App-57x57@2x.png PNG image data, 114 x 114, 8-bit/color RGBA, non-interlaced
Icon-App-60x60@2x.png PNG image data, 120 x 120, 8-bit/color RGBA, non-interlaced
Icon-App-60x60@3x.png PNG image data, 180 x 180, 8-bit/color RGBA, non-interlaced
Icon-App-72x72@1x.png PNG image data, 72 x 72, 8-bit/color RGBA, non-interlaced
Icon-App-72x72@2x.png PNG image data, 144 x 144, 8-bit/color RGBA, non-interlaced
Icon-App-76x76@1x.png PNG image data, 76 x 76, 8-bit/color RGBA, non-interlaced
Icon-App-76x76@2x.png PNG image data, 152 x 152, 8-bit/color RGBA, non-interlaced
Icon-App-83.5x83.5@2x.png PNG image data, 167 x 167, 8-bit/color RGBA, non-interlaced
dir LaunchImage.imageset
Contents.json JSON text data
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
  "images" : [
    {
      "idiom" : "universal",
      "filename" : "LaunchImage.png",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "filename" : "LaunchImage@2x.png",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "filename" : "LaunchImage@3x.png",
      "scale" : "3x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}
LaunchImage.png PNG image data, 1 x 1, 8-bit gray+alpha, non-interlaced
LaunchImage@2x.png PNG image data, 1 x 1, 8-bit gray+alpha, non-interlaced
LaunchImage@3x.png PNG image data, 1 x 1, 8-bit gray+alpha, non-interlaced
README.md ASCII text
1
2
3
4
5
# Launch Screen Assets

You can customize the launch screen with your own desired assets by replacing the image files in this directory.

You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
dir Base.lproj
LaunchScreen.storyboard 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
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="EHf-IW-A2E">
            <objects>
                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
                        <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
                            </imageView>
                        </subviews>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
                            <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
                        </constraints>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="53" y="375"/>
        </scene>
    </scenes>
    <resources>
        <image name="LaunchImage" width="168" height="185"/>
    </resources>
</document>
Main.storyboard 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
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
    </dependencies>
    <scenes>
        <!--Flutter View Controller-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
        </scene>
    </scenes>
</document>
AppDelegate.swift ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}
Info.plist 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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>$(DEVELOPMENT_LANGUAGE)</string>
	<key>CFBundleDisplayName</key>
	<string>15</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>15</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>$(FLUTTER_BUILD_NAME)</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>$(FLUTTER_BUILD_NUMBER)</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>UILaunchStoryboardName</key>
	<string>LaunchScreen</string>
	<key>UIMainStoryboardFile</key>
	<string>Main</string>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UISupportedInterfaceOrientations~ipad</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UIViewControllerBasedStatusBarAppearance</key>
	<false/>
	<key>CADisableMinimumFrameDurationOnPhone</key>
	<true/>
	<key>UIApplicationSupportsIndirectInputEvents</key>
	<true/>
</dict>
</plist>
Runner-Bridging-Header.h ASCII text
1
#import "GeneratedPluginRegistrant.h"
dir Runner.xcodeproj
dir project.xcworkspace
dir xcshareddata
IDEWorkspaceChecks.plist ASCII text
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>
WorkspaceSettings.xcsettings ASCII text
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>PreviewsEnabled</key>
	<false/>
</dict>
</plist>
contents.xcworkspacedata ASCII text
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:">
   </FileRef>
</Workspace>
dir xcshareddata
dir xcschemes
Runner.xcscheme 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
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "1300"
   version = "1.3">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "97C146ED1CF9000F007C117D"
               BuildableName = "Runner.app"
               BlueprintName = "Runner"
               ReferencedContainer = "container:Runner.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES">
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
            BuildableName = "Runner.app"
            BlueprintName = "Runner"
            ReferencedContainer = "container:Runner.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
      <Testables>
         <TestableReference
            skipped = "NO"
            parallelizable = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "331C8080294A63A400263BE5"
               BuildableName = "RunnerTests.xctest"
               BlueprintName = "RunnerTests"
               ReferencedContainer = "container:Runner.xcodeproj">
            </BuildableReference>
         </TestableReference>
      </Testables>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
            BuildableName = "Runner.app"
            BlueprintName = "Runner"
            ReferencedContainer = "container:Runner.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Profile"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
            BuildableName = "Runner.app"
            BlueprintName = "Runner"
            ReferencedContainer = "container:Runner.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>
project.pbxproj 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
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 54;
	objects = {

/* Begin PBXBuildFile section */
		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
		74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
		331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
		331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = 97C146E61CF9000F007C117D /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = 97C146ED1CF9000F007C117D;
			remoteInfo = Runner;
		};
/* End PBXContainerItemProxy section */

/* Begin PBXCopyFilesBuildPhase section */
		9705A1C41CF9048500538489 /* Embed Frameworks */ = {
			isa = PBXCopyFilesBuildPhase;
			buildActionMask = 2147483647;
			dstPath = "";
			dstSubfolderSpec = 10;
			files = (
			);
			name = "Embed Frameworks";
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
		74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
		74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
		97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
		97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
		331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		97C146EB1CF9000F007C117D /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		9740EEB11CF90186004384FC /* Flutter */ = {
			isa = PBXGroup;
			children = (
				3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
				9740EEB21CF90195004384FC /* Debug.xcconfig */,
				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
				9740EEB31CF90195004384FC /* Generated.xcconfig */,
			);
			name = Flutter;
			sourceTree = "<group>";
		};
		331C8082294A63A400263BE5 /* RunnerTests */ = {
			isa = PBXGroup;
			children = (
				331C807B294A618700263BE5 /* RunnerTests.swift */,
			);
			path = RunnerTests;
			sourceTree = "<group>";
		};
		97C146E51CF9000F007C117D = {
			isa = PBXGroup;
			children = (
				9740EEB11CF90186004384FC /* Flutter */,
				97C146F01CF9000F007C117D /* Runner */,
				97C146EF1CF9000F007C117D /* Products */,
				331C8082294A63A400263BE5 /* RunnerTests */,
			);
			sourceTree = "<group>";
		};
		97C146EF1CF9000F007C117D /* Products */ = {
			isa = PBXGroup;
			children = (
				97C146EE1CF9000F007C117D /* Runner.app */,
				331C8081294A63A400263BE5 /* RunnerTests.xctest */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		97C146F01CF9000F007C117D /* Runner */ = {
			isa = PBXGroup;
			children = (
				97C146FA1CF9000F007C117D /* Main.storyboard */,
				97C146FD1CF9000F007C117D /* Assets.xcassets */,
				97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
				97C147021CF9000F007C117D /* Info.plist */,
				1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
				1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
				74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
				74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
			);
			path = Runner;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
		331C8080294A63A400263BE5 /* RunnerTests */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
			buildPhases = (
				331C807D294A63A400263BE5 /* Sources */,
				331C807E294A63A400263BE5 /* Frameworks */,
				331C807F294A63A400263BE5 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
				331C8086294A63A400263BE5 /* PBXTargetDependency */,
			);
			name = RunnerTests;
			productName = RunnerTests;
			productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
			productType = "com.apple.product-type.bundle.unit-test";
		};
		97C146ED1CF9000F007C117D /* Runner */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
			buildPhases = (
				9740EEB61CF901F6004384FC /* Run Script */,
				97C146EA1CF9000F007C117D /* Sources */,
				97C146EB1CF9000F007C117D /* Frameworks */,
				97C146EC1CF9000F007C117D /* Resources */,
				9705A1C41CF9048500538489 /* Embed Frameworks */,
				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = Runner;
			productName = Runner;
			productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
			productType = "com.apple.product-type.application";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		97C146E61CF9000F007C117D /* Project object */ = {
			isa = PBXProject;
			attributes = {
				LastUpgradeCheck = 1300;
				ORGANIZATIONNAME = "";
				TargetAttributes = {
					331C8080294A63A400263BE5 = {
						CreatedOnToolsVersion = 14.0;
						TestTargetID = 97C146ED1CF9000F007C117D;
					};
					97C146ED1CF9000F007C117D = {
						CreatedOnToolsVersion = 7.3.1;
						LastSwiftMigration = 1100;
					};
				};
			};
			buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
			compatibilityVersion = "Xcode 9.3";
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = 97C146E51CF9000F007C117D;
			productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				97C146ED1CF9000F007C117D /* Runner */,
				331C8080294A63A400263BE5 /* RunnerTests */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		331C807F294A63A400263BE5 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		97C146EC1CF9000F007C117D /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
				3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
				97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
				97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
			isa = PBXShellScriptBuildPhase;
			alwaysOutOfDate = 1;
			buildActionMask = 2147483647;
			files = (
			);
			inputPaths = (
				"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
			);
			name = "Thin Binary";
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
		};
		9740EEB61CF901F6004384FC /* Run Script */ = {
			isa = PBXShellScriptBuildPhase;
			alwaysOutOfDate = 1;
			buildActionMask = 2147483647;
			files = (
			);
			inputPaths = (
			);
			name = "Run Script";
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
		};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		331C807D294A63A400263BE5 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		97C146EA1CF9000F007C117D /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
				1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
		331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = 97C146ED1CF9000F007C117D /* Runner */;
			targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
		};
/* End PBXTargetDependency section */

/* Begin PBXVariantGroup section */
		97C146FA1CF9000F007C117D /* Main.storyboard */ = {
			isa = PBXVariantGroup;
			children = (
				97C146FB1CF9000F007C117D /* Base */,
			);
			name = Main.storyboard;
			sourceTree = "<group>";
		};
		97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
			isa = PBXVariantGroup;
			children = (
				97C147001CF9000F007C117D /* Base */,
			);
			name = LaunchScreen.storyboard;
			sourceTree = "<group>";
		};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section */
		249021D3217E4FDB00AE95B9 /* Profile */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
				MTL_ENABLE_DEBUG_INFO = NO;
				SDKROOT = iphoneos;
				SUPPORTED_PLATFORMS = iphoneos;
				TARGETED_DEVICE_FAMILY = "1,2";
				VALIDATE_PRODUCT = YES;
			};
			name = Profile;
		};
		249021D4217E4FDB00AE95B9 /* Profile */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CLANG_ENABLE_MODULES = YES;
				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
				ENABLE_BITCODE = NO;
				INFOPLIST_FILE = Runner/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
				);
				PRODUCT_BUNDLE_IDENTIFIER = com.example.fifteen;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
				SWIFT_VERSION = 5.0;
				VERSIONING_SYSTEM = "apple-generic";
			};
			name = Profile;
		};
		331C8088294A63A400263BE5 /* Debug */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				CODE_SIGN_STYLE = Automatic;
				CURRENT_PROJECT_VERSION = 1;
				GENERATE_INFOPLIST_FILE = YES;
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.example.fifteen.RunnerTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
				SWIFT_VERSION = 5.0;
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
			};
			name = Debug;
		};
		331C8089294A63A400263BE5 /* Release */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				CODE_SIGN_STYLE = Automatic;
				CURRENT_PROJECT_VERSION = 1;
				GENERATE_INFOPLIST_FILE = YES;
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.example.fifteen.RunnerTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_VERSION = 5.0;
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
			};
			name = Release;
		};
		331C808A294A63A400263BE5 /* Profile */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				CODE_SIGN_STYLE = Automatic;
				CURRENT_PROJECT_VERSION = 1;
				GENERATE_INFOPLIST_FILE = YES;
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.example.fifteen.RunnerTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_VERSION = 5.0;
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
			};
			name = Profile;
		};
		97C147031CF9000F007C117D /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
				MTL_ENABLE_DEBUG_INFO = YES;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = iphoneos;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Debug;
		};
		97C147041CF9000F007C117D /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
				MTL_ENABLE_DEBUG_INFO = NO;
				SDKROOT = iphoneos;
				SUPPORTED_PLATFORMS = iphoneos;
				SWIFT_COMPILATION_MODE = wholemodule;
				SWIFT_OPTIMIZATION_LEVEL = "-O";
				TARGETED_DEVICE_FAMILY = "1,2";
				VALIDATE_PRODUCT = YES;
			};
			name = Release;
		};
		97C147061CF9000F007C117D /* Debug */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CLANG_ENABLE_MODULES = YES;
				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
				ENABLE_BITCODE = NO;
				INFOPLIST_FILE = Runner/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
				);
				PRODUCT_BUNDLE_IDENTIFIER = com.example.fifteen;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
				SWIFT_VERSION = 5.0;
				VERSIONING_SYSTEM = "apple-generic";
			};
			name = Debug;
		};
		97C147071CF9000F007C117D /* Release */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CLANG_ENABLE_MODULES = YES;
				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
				ENABLE_BITCODE = NO;
				INFOPLIST_FILE = Runner/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
				);
				PRODUCT_BUNDLE_IDENTIFIER = com.example.fifteen;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
				SWIFT_VERSION = 5.0;
				VERSIONING_SYSTEM = "apple-generic";
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				331C8088294A63A400263BE5 /* Debug */,
				331C8089294A63A400263BE5 /* Release */,
				331C808A294A63A400263BE5 /* Profile */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				97C147031CF9000F007C117D /* Debug */,
				97C147041CF9000F007C117D /* Release */,
				249021D3217E4FDB00AE95B9 /* Profile */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				97C147061CF9000F007C117D /* Debug */,
				97C147071CF9000F007C117D /* Release */,
				249021D4217E4FDB00AE95B9 /* Profile */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */
	};
	rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
dir Runner.xcworkspace
dir xcshareddata
IDEWorkspaceChecks.plist ASCII text
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>
WorkspaceSettings.xcsettings ASCII text
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>PreviewsEnabled</key>
	<false/>
</dict>
</plist>
contents.xcworkspacedata ASCII text
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:Runner.xcodeproj">
   </FileRef>
</Workspace>
dir RunnerTests
RunnerTests.swift ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import Flutter
import UIKit
import XCTest

class RunnerTests: XCTestCase {

  func testExample() {
    // If you add code to the Runner application, consider adding tests here.
    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
  }

}
dir lib
board.dart 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
library;

import "dart:math";

typedef Board = int;
typedef Tile = int;

const Board winningBoard = 0x0FEDCBA987654321;

Tile getTile(Board board, int i) {
  return (board >> (i << 2)) & 0xF;
}

Board setTile(Board board, int i, Tile tile) {
  final bitIdx = i << 2;
  return (board & ~(0xF << bitIdx)) | (tile << bitIdx);
}

Board swapTiles(Board board, int i, int j) {
  int a = getTile(board, i);
  int b = getTile(board, j);
  return setTile(setTile(board, i, b), j, a);
}

Board moveHoleTo(Board board, int newHolePos) {
  int bitPos = (flip(board) & 0xF) << 2;
  int newBitPos = newHolePos << 2;
  int val = (board >> newBitPos) & 0xF;
  return board ^ (val << bitPos) ^ (val << newBitPos);
}

/// Invert ("flip") the permutation of 0 - 15.
Board flip(Board board) {
  Board res = 0;
  for (int i = 0; i < 0x10; i++) {
    final tile = board & 0xF;
    res |= i << (tile << 2);
    board >>= 4;
  }
  return res;
}

bool isSolvable(Board board) {
  int inversions = 0;
  for (int i = 0; i < 0x10; i++) {
    for (int j = i + 1; j < 0x10; j++) {
      final a = getTile(board, i);
      final b = getTile(board, j);
      if (a != 0 && b != 0 && a > b) inversions++;
    }
  }
  final (_, hy) = idxToPos(getTile(flip(board), 0));
  return (inversions + hy).isOdd;
}

String toHex(Board board, [String rowsep = ""]) {
  return Iterable.generate(
      4,
      (y) => Iterable.generate(
              4, (x) => getTile(board, posToIdx(x, y)).toRadixString(0x10))
          .join()).join(rowsep);
}

Board _pack(Iterable<int> nums) {
  assert(nums.length == 0x10);
  return nums.fold(0, (board, tile) => (board << 4) | tile);
}

Board? fromHex(String hex) {
  if (!RegExp(r"^[\da-fA-F]{16}$").hasMatch(hex)) return null;
  return _pack(hex.codeUnits
      .map((c) => int.parse(String.fromCharCode(c), radix: 16))
      .toList()
      .reversed);
}

Board shuffle(Board board, int minMoves, int maxMoves) {
  var holePos = flip(board) & 0xF;
  final rand = Random();
  final moves = rand.nextInt(maxMoves) + minMoves;
  for (int i = 0; i <= moves; i++) {
    List<int> options = [];
    if (holePos > 3) options.add(-4);
    if (holePos < 12) options.add(4);
    int x = holePos & 3;
    if (x > 0) options.add(-1);
    if (x < 3) options.add(1);
    int delta = options[rand.nextInt(options.length)];
    int bitPos = holePos << 2;
    holePos += delta;
    int newBitPos = holePos << 2;
    int val = (board >> newBitPos) & 0xF;
    board ^= (val << bitPos) ^ (val << newBitPos);
  }
  return board;
}

Board randomSolvable() {
  return shuffle(winningBoard, 100, 1000);
}

int posToIdx(int x, int y) {
  return (y << 2) + x;
}

(int, int) idxToPos(int idx) {
  return (idx & 3, idx >> 2);
}

Iterable<Board> solveBFS(int solved, int toSolve, Board start) {
  int flippedMask = (toSolve << 4) | 0xF; // holes matter!
  bool predicate(Board board) => board & toSolve == winningBoard & toSolve;
  if (predicate(start)) return const Iterable.empty();
  List<(Board, Board)> level = [(start, flip(start))];
  Map<Board, Board> predecessor = {flip(start) & flippedMask: 0};
  while (level.isNotEmpty) {
    List<(Board, Board)> nextLevel = [];
    for (final tup in level) {
      final (board, flipped) = tup;
      final holePos = flipped & 0xF;
      Iterable<Board>? move(int dx, int dy) {
        int bitPos = holePos << 2;
        int newHolePos = holePos + posToIdx(dx, dy);
        int newBitPos = newHolePos << 2;
        if ((0xF << newBitPos) & solved != 0) return null;
        int val = (board >> newBitPos) & 0xF;
        final newBoard = board ^ (val << bitPos) ^ (val << newBitPos);
        int valPos = val << 2;
        final newFlipped =
            (((flipped & ~0xF) | newHolePos) & ~(0xF << valPos)) |
                (holePos << valPos);
        if (!predecessor.containsKey(newFlipped & flippedMask)) {
          predecessor[newFlipped & flippedMask] = board;
          if (predicate(newBoard)) {
            List<Board> solution = [newBoard];
            Board preBoard = board;
            while (preBoard != start) {
              solution.add(preBoard);
              preBoard = predecessor[flip(preBoard) & flippedMask]!;
            }
            return solution.reversed;
          }
          nextLevel.add((newBoard, newFlipped));
        }
        return null;
      }

      // Hot loop + no macros = ugly boilerplate (or error abuse or the like)
      final (hx, hy) = idxToPos(holePos);
      if (hx > 0) {
        final res = move(-1, 0);
        if (res != null) return res;
      }
      if (hx < 3) {
        final res = move(1, 0);
        if (res != null) return res;
      }
      if (hy > 0) {
        final res = move(0, -1);
        if (res != null) return res;
      }
      if (hy < 3) {
        final res = move(0, 1);
        if (res != null) return res;
      }
    }
    level = nextLevel;
  }
  throw "unsolvable";
}

Iterable<Board> solve(Board start) {
  const row1 = 0xFFFF;
  const row2 = row1 << 16;
  const upperHalf = row2 | row1;

  final row1Sol = solveBFS(0, row1, start);
  final row2Sol = solveBFS(row1, upperHalf, row1Sol.lastOrNull ?? start);
  final lowerSolution = solveBFS(
      upperHalf, ~0, row2Sol.lastOrNull ?? row1Sol.lastOrNull ?? start);

  return row1Sol.followedBy(row2Sol).followedBy(lowerSolution);
}
main.dart 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
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';

import 'board.dart' as brd;

typedef Board = brd.Board;
typedef Tile = brd.Tile;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '15',
      theme: ThemeData(
        // Define the default brightness and colors.
        brightness: Brightness.light,
        colorSchemeSeed: Colors.lightBlueAccent,
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class _TileWidget extends StatelessWidget {
  final Tile tile;
  final Function()? onPressed;
  final Color? disabledBackgroundColor;
  final bool showBlank;
  const _TileWidget(this.tile, this.onPressed,
      {this.disabledBackgroundColor = const Color.fromARGB(255, 134, 198, 238),
      this.showBlank = false});
  @override
  Widget build(BuildContext context) {
    if (tile == 0 && !showBlank) return Container();
    return Container(
        padding: const EdgeInsets.all(2),
        child: FilledButton(
          key: ValueKey<int>(tile),
          onPressed: onPressed,
          style: FilledButton.styleFrom(
              backgroundColor: const Color.fromARGB(255, 67, 170, 230),
              disabledBackgroundColor: disabledBackgroundColor),
          child: Text(tile == 0 ? "" : tile.toString(),
              style: DefaultTextStyle.of(context)
                  .style
                  .apply(fontSizeFactor: 2.0)),
        ));
  }
}

class _DraggableTileWidget extends StatelessWidget {
  final Tile tile;
  final int idx;
  final Function(Tile) onAccept;
  const _DraggableTileWidget(this.tile, this.idx, this.onAccept);
  @override
  Widget build(BuildContext context) {
    final Widget child = _TileWidget(tile, null, showBlank: true);
    return DragTarget<int>(
      builder: (context, candidateData, rejectedData) => LayoutBuilder(
          builder: (context, constraints) => Draggable<int>(
                data: idx,
                dragAnchorStrategy: pointerDragAnchorStrategy,
                feedback: SizedBox(
                    width: constraints.maxWidth,
                    height: constraints.maxHeight,
                    child: DefaultTextStyle.of(context).wrap(context, child)),
                childWhenDragging: _TileWidget(
                  tile,
                  null,
                  disabledBackgroundColor:
                      const Color.fromARGB(255, 190, 217, 233),
                  showBlank: true,
                ),
                child: child,
              )),
      onAccept: onAccept,
    );
  }
}

class _GameWidget extends StatefulWidget {
  final SharedPreferences? prefs;
  const _GameWidget(this.prefs);
  @override
  State<_GameWidget> createState() => _GameWidgetState();
}

class _GameWidgetState extends State<_GameWidget> {
  Board _board;
  Board get board => _board;
  bool get won => board == brd.winningBoard;
  set board(Board board) {
    _board = board;
    _persist();
  }

  bool _editing = false;
  bool get editing => _editing;
  set editing(bool editing) {
    _editing = editing;
    _persist();
  }

  // These two are deliberately not persisted
  Future<List<Board>>? _solving;
  Timer? _solutionStepper;

  _GameWidgetState() : _board = brd.randomSolvable();

  @override
  initState() {
    super.initState();
    if (widget.prefs == null) return;
    final SharedPreferences prefs = widget.prefs!;
    _editing = prefs.getBool('editing') ?? false;
    final boardHex = prefs.getString('board');
    if (boardHex != null) _board = brd.fromHex(boardHex) ?? _board;
    _persist();
  }

  _persist() {
    if (widget.prefs == null) return;
    final SharedPreferences prefs = widget.prefs!;
    prefs.setBool('editing', editing).then((success) {
      if (success) prefs.setString('board', brd.toHex(board));
      // TODO (...) log failure or notify user
    });
  }

  _move(int toIdx) {
    setState(() {
      board = brd.moveHoleTo(board, toIdx);
    });
  }

  _slide(int dx, int dy) {
    if (_solutionStepper != null || _solving != null || editing) return;
    var (hx, hy) = brd.idxToPos(brd.getTile(brd.flip(board), 0));
    // It is more intuitive to slide in the opposite direction of hole movement.
    hx -= dx;
    hy -= dy;
    if (hx < 0 || hx > 3 || hy < 0 || hy > 3) return;
    _move(brd.posToIdx(hx, hy));
  }

  _solve() {
    setState(() {
      _solving = compute((board) => brd.solve(board).toList(), board);
    });
  }

  _stop() {
    if (_solutionStepper == null) return;
    setState(() {
      _solutionStepper!.cancel();
      _solutionStepper = null;
    });
  }

  _shuffle() {
    setState(() {
      board = brd.randomSolvable();
      _solving = null;
      if (_solutionStepper != null) {
        _solutionStepper!.cancel();
        _solutionStepper = null;
      }
    });
  }

  _edit() {
    _stop();
    setState(() {
      editing = true;
    });
  }

  _done() {
    setState(() {
      editing = false;
    });
  }

  _copy() {
    Clipboard.setData(ClipboardData(text: brd.toHex(board)));
  }

  _paste() {
    Clipboard.getData(Clipboard.kTextPlain).then((value) {
      final text = value?.text;
      if (text == null) return;
      setState(() {
        final clippedboard = brd.fromHex(text);
        if (clippedboard != null) board = clippedboard;
      });
    });
  }

  _reset() {
    setState(() {
      board = brd.winningBoard;
    });
  }

  _swap(int i, j) {
    setState(() {
      board = brd.swapTiles(board, i, j);
    });
  }

  Widget _build() {
    final (hx, hy) = brd.idxToPos(brd.flip(board) & 0xF);
    final buttons = Iterable.generate(
        4,
        (y) => Iterable.generate(4, (x) {
              final idx = brd.posToIdx(x, y);
              Tile tile = brd.getTile(board, idx);
              if (editing) {
                return _DraggableTileWidget(
                  tile,
                  idx,
                  (int j) => _swap(idx, j),
                );
              }
              final neighbor = (x - hx).abs() + (y - hy).abs() == 1;
              return _TileWidget(
                tile,
                neighbor && _solutionStepper == null
                    ? () {
                        _move(idx);
                      }
                    : null,
              );
            }).toList()).toList();
    final grid = GridView.count(
      crossAxisCount: 4,
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
      children: buttons.expand((x) => x).toList(),
    );
    return Column(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: editing
              ? [
                  TextButton(onPressed: _shuffle, child: const Text("Shuffle")),
                  TextButton(onPressed: _reset, child: const Text("Reset")),
                  TextButton(
                      // TODO (...?) disable if clipboard content is invalid
                      onPressed: _paste,
                      child: const Text("Paste")),
                  FilledButton(
                      onPressed: brd.isSolvable(board) ? _done : null,
                      child: const Text("Done")),
                ]
              : [
                  TextButton(onPressed: _shuffle, child: const Text("Shuffle")),
                  TextButton(onPressed: _edit, child: const Text("Edit")),
                  TextButton(
                      onPressed: brd.isSolvable(board) ? _copy : null,
                      child: const Text("Copy")),
                  _solutionStepper == null
                      ? FilledButton(
                          onPressed: won ? null : _solve,
                          child: const Text("Solve"))
                      : FilledButton(
                          onPressed: won ? null : _stop,
                          child: const Text("Stop")),
                ],
        ),
        Expanded(
            child: Center(
                child: Focus(
          autofocus: true,
          onKeyEvent: (_, keyEvent) {
            if (keyEvent is! KeyUpEvent) return KeyEventResult.ignored;
            switch (keyEvent.logicalKey) {
              case LogicalKeyboardKey.keyW:
                _slide(0, -1);
              case LogicalKeyboardKey.keyA:
                _slide(-1, 0);
              case LogicalKeyboardKey.keyS:
                _slide(0, 1);
              case LogicalKeyboardKey.keyD:
                _slide(1, 0);
              default:
                return KeyEventResult.ignored;
            }
            return KeyEventResult.handled;
          },
          child: GestureDetector(
              // TODO make sure that this fires only for the primary axis
              onHorizontalDragEnd: (DragEndDetails details) {
                if (details.primaryVelocity == null) return;
                _slide(details.primaryVelocity!.sign.toInt(), 0);
              },
              onVerticalDragEnd: (DragEndDetails details) {
                if (details.primaryVelocity == null) return;
                _slide(0, details.primaryVelocity!.sign.toInt());
              },
              child: grid),
        ))),
        TextButton(
            onPressed: () {
              launchUrl(Uri.parse("https://en.wikipedia.org/wiki/15_puzzle"),
                  mode: LaunchMode.externalApplication);
            },
            child: const Text("About")),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    if (_solving != null) {
      return FutureBuilder<List<Board>>(
        future: _solving,
        builder: (context, snapshot) {
          assert(!snapshot.hasError);
          if (snapshot.hasData) {
            _solving = null;
            final data = snapshot.data!;
            assert(data.isNotEmpty);
            int i = 0;
            _solutionStepper = Timer.periodic(
                const Duration(seconds: 1),
                (Timer t) => setState(() {
                      board = data[i];
                      i++;
                      if (i >= data.length) {
                        _solutionStepper!.cancel();
                        _solutionStepper = null;
                      }
                    }));
            return _build();
          }
          return const Center(child: CircularProgressIndicator());
        },
      );
    }
    return _build();
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  _MyHomePageState();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Container(
      padding:
          EdgeInsets.fromLTRB(0, MediaQuery.of(context).viewPadding.top, 0, 0),
      child: FutureBuilder<SharedPreferences>(
        future: SharedPreferences.getInstance(),
        builder: (context, snapshot) {
          // Behave gracefully if we don't have persistent storage.
          // TODO (...) log this or notify user about the issue
          if (snapshot.hasError) return const _GameWidget(null);
          if (snapshot.hasData) return _GameWidget(snapshot.data!);
          return const Center(child: CircularProgressIndicator());
        },
      ),
    ));
  }
}
dir linux
dir flutter
CMakeLists.txt 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
# This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.10)

set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")

# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)

# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.

# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX)
    set(NEW_LIST "")
    foreach(element ${${LIST_NAME}})
        list(APPEND NEW_LIST "${PREFIX}${element}")
    endforeach(element)
    set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction()

# === Flutter Library ===
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)

set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")

# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)

list(APPEND FLUTTER_LIBRARY_HEADERS
  "fl_basic_message_channel.h"
  "fl_binary_codec.h"
  "fl_binary_messenger.h"
  "fl_dart_project.h"
  "fl_engine.h"
  "fl_json_message_codec.h"
  "fl_json_method_codec.h"
  "fl_message_codec.h"
  "fl_method_call.h"
  "fl_method_channel.h"
  "fl_method_codec.h"
  "fl_method_response.h"
  "fl_plugin_registrar.h"
  "fl_plugin_registry.h"
  "fl_standard_message_codec.h"
  "fl_standard_method_codec.h"
  "fl_string_codec.h"
  "fl_value.h"
  "fl_view.h"
  "flutter_linux.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
  "${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE
  PkgConfig::GTK
  PkgConfig::GLIB
  PkgConfig::GIO
)
add_dependencies(flutter flutter_assemble)

# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
add_custom_command(
  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
    ${CMAKE_CURRENT_BINARY_DIR}/_phony_
  COMMAND ${CMAKE_COMMAND} -E env
    ${FLUTTER_TOOL_ENVIRONMENT}
    "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
      ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
  VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
  "${FLUTTER_LIBRARY}"
  ${FLUTTER_LIBRARY_HEADERS}
)
generated_plugin_registrant.cc ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
//
//  Generated file. Do not edit.
//

// clang-format off

#include "generated_plugin_registrant.h"

#include <url_launcher_linux/url_launcher_plugin.h>

void fl_register_plugins(FlPluginRegistry* registry) {
  g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
      fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
  url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}
generated_plugin_registrant.h ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
//
//  Generated file. Do not edit.
//

// clang-format off

#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_

#include <flutter_linux/flutter_linux.h>

// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);

#endif  // GENERATED_PLUGIN_REGISTRANT_
generated_plugins.cmake 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
#
# Generated file, do not edit.
#

list(APPEND FLUTTER_PLUGIN_LIST
  url_launcher_linux
)

list(APPEND FLUTTER_FFI_PLUGIN_LIST
)

set(PLUGIN_BUNDLED_LIBRARIES)

foreach(plugin ${FLUTTER_PLUGIN_LIST})
  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)

foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)
CMakeLists.txt 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
# Project-level configuration.
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)

# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "fifteen")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.example.fifteen")

# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)

# Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")

# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
  set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
  set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
  set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
  set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
  set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
  set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()

# Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  set(CMAKE_BUILD_TYPE "Debug" CACHE
    STRING "Flutter build mode" FORCE)
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
    "Debug" "Profile" "Release")
endif()

# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
  target_compile_features(${TARGET} PUBLIC cxx_std_14)
  target_compile_options(${TARGET} PRIVATE -Wall -Werror)
  target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
  target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()

# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})

# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)

add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")

# Define the application target. To change its name, change BINARY_NAME above,
# not the value here, or `flutter run` will no longer work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
  "main.cc"
  "my_application.cc"
  "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)

# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})

# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)

# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)

# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
  PROPERTIES
  RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)


# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)


# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
  set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()

# Start with a clean build bundle directory every time.
install(CODE "
  file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
  " COMPONENT Runtime)

set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")

install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
  COMPONENT Runtime)

install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
  COMPONENT Runtime)

install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
  COMPONENT Runtime)

foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
  install(FILES "${bundled_library}"
    DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
    COMPONENT Runtime)
endforeach(bundled_library)

# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
  file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
  " COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
  DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)

# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
  install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
    COMPONENT Runtime)
endif()
main.cc ASCII text
1
2
3
4
5
6
#include "my_application.h"

int main(int argc, char** argv) {
  g_autoptr(MyApplication) app = my_application_new();
  return g_application_run(G_APPLICATION(app), argc, argv);
}
my_application.cc 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
#include "my_application.h"

#include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif

#include "flutter/generated_plugin_registrant.h"

struct _MyApplication {
  GtkApplication parent_instance;
  char** dart_entrypoint_arguments;
};

G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)

// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
  MyApplication* self = MY_APPLICATION(application);
  GtkWindow* window =
      GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));

  // Use a header bar when running in GNOME as this is the common style used
  // by applications and is the setup most users will be using (e.g. Ubuntu
  // desktop).
  // If running on X and not using GNOME then just use a traditional title bar
  // in case the window manager does more exotic layout, e.g. tiling.
  // If running on Wayland assume the header bar will work (may need changing
  // if future cases occur).
  gboolean use_header_bar = TRUE;
#ifdef GDK_WINDOWING_X11
  GdkScreen* screen = gtk_window_get_screen(window);
  if (GDK_IS_X11_SCREEN(screen)) {
    const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
    if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
      use_header_bar = FALSE;
    }
  }
#endif
  if (use_header_bar) {
    GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
    gtk_widget_show(GTK_WIDGET(header_bar));
    gtk_header_bar_set_title(header_bar, "15");
    gtk_header_bar_set_show_close_button(header_bar, TRUE);
    gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
  } else {
    gtk_window_set_title(window, "15");
  }

  gtk_window_set_default_size(window, 405, 720);
  gtk_widget_show(GTK_WIDGET(window));

  g_autoptr(FlDartProject) project = fl_dart_project_new();
  fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);

  FlView* view = fl_view_new(project);
  gtk_widget_show(GTK_WIDGET(view));
  gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));

  fl_register_plugins(FL_PLUGIN_REGISTRY(view));

  gtk_widget_grab_focus(GTK_WIDGET(view));
}

// Implements GApplication::local_command_line.
static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
  MyApplication* self = MY_APPLICATION(application);
  // Strip out the first argument as it is the binary name.
  self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);

  g_autoptr(GError) error = nullptr;
  if (!g_application_register(application, nullptr, &error)) {
     g_warning("Failed to register: %s", error->message);
     *exit_status = 1;
     return TRUE;
  }

  g_application_activate(application);
  *exit_status = 0;

  return TRUE;
}

// Implements GObject::dispose.
static void my_application_dispose(GObject* object) {
  MyApplication* self = MY_APPLICATION(object);
  g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
  G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}

static void my_application_class_init(MyApplicationClass* klass) {
  G_APPLICATION_CLASS(klass)->activate = my_application_activate;
  G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
  G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}

static void my_application_init(MyApplication* self) {}

MyApplication* my_application_new() {
  return MY_APPLICATION(g_object_new(my_application_get_type(),
                                     "application-id", APPLICATION_ID,
                                     "flags", G_APPLICATION_NON_UNIQUE,
                                     nullptr));
}
my_application.h ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_

#include <gtk/gtk.h>

G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
                     GtkApplication)

/**
 * my_application_new:
 *
 * Creates a new Flutter-based application.
 *
 * Returns: a new #MyApplication.
 */
MyApplication* my_application_new();

#endif  // FLUTTER_MY_APPLICATION_H_
dir macos
dir Flutter
Flutter-Debug.xcconfig ASCII text
1
#include "ephemeral/Flutter-Generated.xcconfig"
Flutter-Release.xcconfig ASCII text
1
#include "ephemeral/Flutter-Generated.xcconfig"
GeneratedPluginRegistrant.swift ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//
//  Generated file. Do not edit.
//

import FlutterMacOS
import Foundation

import shared_preferences_foundation
import url_launcher_macos

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
  SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
  UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}
dir Runner
dir Assets.xcassets
dir AppIcon.appiconset
Contents.json JSON text data
 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
{
    "info": {
        "version": 1,
        "author": "xcode"
    },
    "images": [
        {
            "size": "16x16",
            "idiom": "mac",
            "filename": "app_icon_16.png",
            "scale": "1x"
        },
        {
            "size": "16x16",
            "idiom": "mac",
            "filename": "app_icon_32.png",
            "scale": "2x"
        },
        {
            "size": "32x32",
            "idiom": "mac",
            "filename": "app_icon_32.png",
            "scale": "1x"
        },
        {
            "size": "32x32",
            "idiom": "mac",
            "filename": "app_icon_64.png",
            "scale": "2x"
        },
        {
            "size": "128x128",
            "idiom": "mac",
            "filename": "app_icon_128.png",
            "scale": "1x"
        },
        {
            "size": "128x128",
            "idiom": "mac",
            "filename": "app_icon_256.png",
            "scale": "2x"
        },
        {
            "size": "256x256",
            "idiom": "mac",
            "filename": "app_icon_256.png",
            "scale": "1x"
        },
        {
            "size": "256x256",
            "idiom": "mac",
            "filename": "app_icon_512.png",
            "scale": "2x"
        },
        {
            "size": "512x512",
            "idiom": "mac",
            "filename": "app_icon_512.png",
            "scale": "1x"
        },
        {
            "size": "512x512",
            "idiom": "mac",
            "filename": "app_icon_1024.png",
            "scale": "2x"
        }
    ]
}
app_icon_1024.png PNG image data, 1024 x 1024, 8-bit/color RGBA, non-interlaced
app_icon_128.png PNG image data, 128 x 128, 8-bit/color RGBA, non-interlaced
app_icon_16.png PNG image data, 16 x 16, 8-bit/color RGBA, non-interlaced
app_icon_256.png PNG image data, 256 x 256, 8-bit/color RGBA, non-interlaced
app_icon_32.png PNG image data, 32 x 32, 8-bit/color RGBA, non-interlaced
app_icon_512.png PNG image data, 512 x 512, 8-bit/color RGBA, non-interlaced
app_icon_64.png PNG image data, 64 x 64, 8-bit/color RGBA, non-interlaced
dir Base.lproj
MainMenu.xib 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
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
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
    <dependencies>
        <deployment identifier="macosx"/>
        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
            <connections>
                <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
            </connections>
        </customObject>
        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
        <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Runner" customModuleProvider="target">
            <connections>
                <outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/>
                <outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
            </connections>
        </customObject>
        <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
        <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
            <items>
                <menuItem title="APP_NAME" id="1Xt-HY-uBw">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr">
                        <items>
                            <menuItem title="About APP_NAME" id="5kV-Vb-QxS">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <connections>
                                    <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
                                </connections>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
                            <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
                            <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
                            <menuItem title="Services" id="NMo-om-nkz">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
                            <menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
                                <connections>
                                    <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                <connections>
                                    <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Show All" id="Kd2-mp-pUS">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <connections>
                                    <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
                                </connections>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
                            <menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi">
                                <connections>
                                    <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
                                </connections>
                            </menuItem>
                        </items>
                    </menu>
                </menuItem>
                <menuItem title="Edit" id="5QF-Oa-p0T">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <menu key="submenu" title="Edit" id="W48-6f-4Dl">
                        <items>
                            <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
                                <connections>
                                    <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
                                <connections>
                                    <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
                                </connections>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
                            <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
                                <connections>
                                    <action selector="cut:" target="-1" id="YJe-68-I9s"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
                                <connections>
                                    <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
                                <connections>
                                    <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                <connections>
                                    <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Delete" id="pa3-QI-u2k">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <connections>
                                    <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
                                <connections>
                                    <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
                                </connections>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
                            <menuItem title="Find" id="4EN-yA-p0u">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Find" id="1b7-l0-nxx">
                                    <items>
                                        <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
                                            <connections>
                                                <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                            <connections>
                                                <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
                                            <connections>
                                                <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
                                            <connections>
                                                <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
                                            <connections>
                                                <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
                                            <connections>
                                                <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
                                    <items>
                                        <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
                                            <connections>
                                                <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
                                            <connections>
                                                <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
                                        <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Substitutions" id="9ic-FL-obx">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
                                    <items>
                                        <menuItem title="Show Substitutions" id="z6F-FW-3nz">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
                                        <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Smart Quotes" id="hQb-2v-fYv">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Smart Dashes" id="rgM-f4-ycn">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Smart Links" id="cwL-P1-jid">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Data Detectors" id="tRr-pd-1PS">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Text Replacement" id="HFQ-gK-NFA">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Transformations" id="2oI-Rn-ZJC">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
                                    <items>
                                        <menuItem title="Make Upper Case" id="vmV-6d-7jI">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Make Lower Case" id="d9M-CD-aMd">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Capitalize" id="UEZ-Bs-lqG">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Speech" id="xrE-MZ-jX0">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
                                    <items>
                                        <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                        </items>
                    </menu>
                </menuItem>
                <menuItem title="View" id="H8h-7b-M4v">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <menu key="submenu" title="View" id="HyV-fh-RgO">
                        <items>
                            <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
                                <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
                                <connections>
                                    <action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
                                </connections>
                            </menuItem>
                        </items>
                    </menu>
                </menuItem>
                <menuItem title="Window" id="aUF-d1-5bR">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
                        <items>
                            <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
                                <connections>
                                    <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Zoom" id="R4o-n2-Eq4">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <connections>
                                    <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
                                </connections>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
                            <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <connections>
                                    <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
                                </connections>
                            </menuItem>
                        </items>
                    </menu>
                </menuItem>
                <menuItem title="Help" id="EPT-qC-fAb">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <menu key="submenu" title="Help" systemMenu="help" id="rJ0-wn-3NY"/>
                </menuItem>
            </items>
            <point key="canvasLocation" x="142" y="-258"/>
        </menu>
        <window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="Runner" customModuleProvider="target">
            <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
            <rect key="contentRect" x="335" y="390" width="405" height="720"/>
            <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
            <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
                <rect key="frame" x="0.0" y="0.0" width="405" height="720"/>
                <autoresizingMask key="autoresizingMask"/>
            </view>
        </window>
    </objects>
</document>
dir Configs
AppInfo.xcconfig Unicode text, UTF-8 text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Application-level settings for the Runner target.
//
// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
// future. If not, the values below would default to using the project name when this becomes a
// 'flutter create' template.

// The application's name. By default this is also the title of the Flutter window.
PRODUCT_NAME = 15

// The application's bundle identifier
PRODUCT_BUNDLE_IDENTIFIER = com.example.fifteen

// The copyright displayed in application information
// TODO update this
PRODUCT_COPYRIGHT = Copyright © 2023
Debug.xcconfig ASCII text
1
2
#include "../../Flutter/Flutter-Debug.xcconfig"
#include "Warnings.xcconfig"
Release.xcconfig ASCII text
1
2
#include "../../Flutter/Flutter-Release.xcconfig"
#include "Warnings.xcconfig"
Warnings.xcconfig ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
GCC_WARN_UNDECLARED_SELECTOR = YES
CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
CLANG_WARN_PRAGMA_PACK = YES
CLANG_WARN_STRICT_PROTOTYPES = YES
CLANG_WARN_COMMA = YES
GCC_WARN_STRICT_SELECTOR_MATCH = YES
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
GCC_WARN_SHADOW = YES
CLANG_WARN_UNREACHABLE_CODE = YES
AppDelegate.swift ASCII text
1
2
3
4
5
6
7
8
9
import Cocoa
import FlutterMacOS

@NSApplicationMain
class AppDelegate: FlutterAppDelegate {
  override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
    return true
  }
}
DebugProfile.entitlements ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.security.app-sandbox</key>
	<true/>
	<key>com.apple.security.cs.allow-jit</key>
	<true/>
	<key>com.apple.security.network.server</key>
	<true/>
</dict>
</plist>
Info.plist 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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>$(DEVELOPMENT_LANGUAGE)</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIconFile</key>
	<string></string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>$(FLUTTER_BUILD_NAME)</string>
	<key>CFBundleVersion</key>
	<string>$(FLUTTER_BUILD_NUMBER)</string>
	<key>LSMinimumSystemVersion</key>
	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
	<key>NSHumanReadableCopyright</key>
	<string>$(PRODUCT_COPYRIGHT)</string>
	<key>NSMainNibFile</key>
	<string>MainMenu</string>
	<key>NSPrincipalClass</key>
	<string>NSApplication</string>
</dict>
</plist>
MainFlutterWindow.swift ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import Cocoa
import FlutterMacOS

class MainFlutterWindow: NSWindow {
  override func awakeFromNib() {
    let flutterViewController = FlutterViewController()
    let windowFrame = self.frame
    self.contentViewController = flutterViewController
    self.setFrame(windowFrame, display: true)

    RegisterGeneratedPlugins(registry: flutterViewController)

    super.awakeFromNib()
  }
}
Release.entitlements ASCII text
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.security.app-sandbox</key>
	<true/>
</dict>
</plist>
dir Runner.xcodeproj
dir project.xcworkspace
dir xcshareddata
IDEWorkspaceChecks.plist ASCII text
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>
dir xcshareddata
dir xcschemes
Runner.xcscheme 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
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "1300"
   version = "1.3">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "33CC10EC2044A3C60003C045"
               BuildableName = "fifteen.app"
               BlueprintName = "Runner"
               ReferencedContainer = "container:Runner.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES">
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
            BuildableName = "fifteen.app"
            BlueprintName = "Runner"
            ReferencedContainer = "container:Runner.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
      <Testables>
         <TestableReference
            skipped = "NO"
            parallelizable = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "331C80D4294CF70F00263BE5"
               BuildableName = "RunnerTests.xctest"
               BlueprintName = "RunnerTests"
               ReferencedContainer = "container:Runner.xcodeproj">
            </BuildableReference>
         </TestableReference>
      </Testables>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
            BuildableName = "fifteen.app"
            BlueprintName = "Runner"
            ReferencedContainer = "container:Runner.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Profile"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
            BuildableName = "fifteen.app"
            BlueprintName = "Runner"
            ReferencedContainer = "container:Runner.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>
project.pbxproj 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
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 54;
	objects = {

/* Begin PBXAggregateTarget section */
		33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
			isa = PBXAggregateTarget;
			buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
			buildPhases = (
				33CC111E2044C6BF0003C045 /* ShellScript */,
			);
			dependencies = (
			);
			name = "Flutter Assemble";
			productName = FLX;
		};
/* End PBXAggregateTarget section */

/* Begin PBXBuildFile section */
		331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
		335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
		33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
		33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
		33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
		33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
		331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = 33CC10EC2044A3C60003C045;
			remoteInfo = Runner;
		};
		33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = 33CC111A2044C6BA0003C045;
			remoteInfo = FLX;
		};
/* End PBXContainerItemProxy section */

/* Begin PBXCopyFilesBuildPhase section */
		33CC110E2044A8840003C045 /* Bundle Framework */ = {
			isa = PBXCopyFilesBuildPhase;
			buildActionMask = 2147483647;
			dstPath = "";
			dstSubfolderSpec = 10;
			files = (
			);
			name = "Bundle Framework";
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
		331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
		331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
		333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
		335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
		33CC10ED2044A3C60003C045 /* fifteen.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "fifteen.app"; sourceTree = BUILT_PRODUCTS_DIR; };
		33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
		33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
		33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
		33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
		33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
		33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
		33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
		33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
		33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
		33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
		33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		331C80D2294CF70F00263BE5 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		33CC10EA2044A3C60003C045 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		331C80D6294CF71000263BE5 /* RunnerTests */ = {
			isa = PBXGroup;
			children = (
				331C80D7294CF71000263BE5 /* RunnerTests.swift */,
			);
			path = RunnerTests;
			sourceTree = "<group>";
		};
		33BA886A226E78AF003329D5 /* Configs */ = {
			isa = PBXGroup;
			children = (
				33E5194F232828860026EE4D /* AppInfo.xcconfig */,
				9740EEB21CF90195004384FC /* Debug.xcconfig */,
				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
				333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
			);
			path = Configs;
			sourceTree = "<group>";
		};
		33CC10E42044A3C60003C045 = {
			isa = PBXGroup;
			children = (
				33FAB671232836740065AC1E /* Runner */,
				33CEB47122A05771004F2AC0 /* Flutter */,
				331C80D6294CF71000263BE5 /* RunnerTests */,
				33CC10EE2044A3C60003C045 /* Products */,
				D73912EC22F37F3D000D13A0 /* Frameworks */,
			);
			sourceTree = "<group>";
		};
		33CC10EE2044A3C60003C045 /* Products */ = {
			isa = PBXGroup;
			children = (
				33CC10ED2044A3C60003C045 /* fifteen.app */,
				331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		33CC11242044D66E0003C045 /* Resources */ = {
			isa = PBXGroup;
			children = (
				33CC10F22044A3C60003C045 /* Assets.xcassets */,
				33CC10F42044A3C60003C045 /* MainMenu.xib */,
				33CC10F72044A3C60003C045 /* Info.plist */,
			);
			name = Resources;
			path = ..;
			sourceTree = "<group>";
		};
		33CEB47122A05771004F2AC0 /* Flutter */ = {
			isa = PBXGroup;
			children = (
				335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
				33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
				33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
				33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
			);
			path = Flutter;
			sourceTree = "<group>";
		};
		33FAB671232836740065AC1E /* Runner */ = {
			isa = PBXGroup;
			children = (
				33CC10F02044A3C60003C045 /* AppDelegate.swift */,
				33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
				33E51913231747F40026EE4D /* DebugProfile.entitlements */,
				33E51914231749380026EE4D /* Release.entitlements */,
				33CC11242044D66E0003C045 /* Resources */,
				33BA886A226E78AF003329D5 /* Configs */,
			);
			path = Runner;
			sourceTree = "<group>";
		};
		D73912EC22F37F3D000D13A0 /* Frameworks */ = {
			isa = PBXGroup;
			children = (
			);
			name = Frameworks;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
		331C80D4294CF70F00263BE5 /* RunnerTests */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
			buildPhases = (
				331C80D1294CF70F00263BE5 /* Sources */,
				331C80D2294CF70F00263BE5 /* Frameworks */,
				331C80D3294CF70F00263BE5 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
				331C80DA294CF71000263BE5 /* PBXTargetDependency */,
			);
			name = RunnerTests;
			productName = RunnerTests;
			productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
			productType = "com.apple.product-type.bundle.unit-test";
		};
		33CC10EC2044A3C60003C045 /* Runner */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
			buildPhases = (
				33CC10E92044A3C60003C045 /* Sources */,
				33CC10EA2044A3C60003C045 /* Frameworks */,
				33CC10EB2044A3C60003C045 /* Resources */,
				33CC110E2044A8840003C045 /* Bundle Framework */,
				3399D490228B24CF009A79C7 /* ShellScript */,
			);
			buildRules = (
			);
			dependencies = (
				33CC11202044C79F0003C045 /* PBXTargetDependency */,
			);
			name = Runner;
			productName = Runner;
			productReference = 33CC10ED2044A3C60003C045 /* fifteen.app */;
			productType = "com.apple.product-type.application";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		33CC10E52044A3C60003C045 /* Project object */ = {
			isa = PBXProject;
			attributes = {
				LastSwiftUpdateCheck = 0920;
				LastUpgradeCheck = 1300;
				ORGANIZATIONNAME = "";
				TargetAttributes = {
					331C80D4294CF70F00263BE5 = {
						CreatedOnToolsVersion = 14.0;
						TestTargetID = 33CC10EC2044A3C60003C045;
					};
					33CC10EC2044A3C60003C045 = {
						CreatedOnToolsVersion = 9.2;
						LastSwiftMigration = 1100;
						ProvisioningStyle = Automatic;
						SystemCapabilities = {
							com.apple.Sandbox = {
								enabled = 1;
							};
						};
					};
					33CC111A2044C6BA0003C045 = {
						CreatedOnToolsVersion = 9.2;
						ProvisioningStyle = Manual;
					};
				};
			};
			buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
			compatibilityVersion = "Xcode 9.3";
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = 33CC10E42044A3C60003C045;
			productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				33CC10EC2044A3C60003C045 /* Runner */,
				331C80D4294CF70F00263BE5 /* RunnerTests */,
				33CC111A2044C6BA0003C045 /* Flutter Assemble */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		331C80D3294CF70F00263BE5 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		33CC10EB2044A3C60003C045 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
				33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
		3399D490228B24CF009A79C7 /* ShellScript */ = {
			isa = PBXShellScriptBuildPhase;
			alwaysOutOfDate = 1;
			buildActionMask = 2147483647;
			files = (
			);
			inputFileListPaths = (
			);
			inputPaths = (
			);
			outputFileListPaths = (
			);
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
		};
		33CC111E2044C6BF0003C045 /* ShellScript */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputFileListPaths = (
				Flutter/ephemeral/FlutterInputs.xcfilelist,
			);
			inputPaths = (
				Flutter/ephemeral/tripwire,
			);
			outputFileListPaths = (
				Flutter/ephemeral/FlutterOutputs.xcfilelist,
			);
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
		};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		331C80D1294CF70F00263BE5 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		33CC10E92044A3C60003C045 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
				33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
				335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
		331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = 33CC10EC2044A3C60003C045 /* Runner */;
			targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
		};
		33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
			targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
		};
/* End PBXTargetDependency section */

/* Begin PBXVariantGroup section */
		33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
			isa = PBXVariantGroup;
			children = (
				33CC10F52044A3C60003C045 /* Base */,
			);
			name = MainMenu.xib;
			path = Runner;
			sourceTree = "<group>";
		};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section */
		331C80DB294CF71000263BE5 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				CURRENT_PROJECT_VERSION = 1;
				GENERATE_INFOPLIST_FILE = YES;
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.example.fifteen.RunnerTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_VERSION = 5.0;
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/fifteen.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/fifteen";
			};
			name = Debug;
		};
		331C80DC294CF71000263BE5 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				CURRENT_PROJECT_VERSION = 1;
				GENERATE_INFOPLIST_FILE = YES;
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.example.fifteen.RunnerTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_VERSION = 5.0;
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/fifteen.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/fifteen";
			};
			name = Release;
		};
		331C80DD294CF71000263BE5 /* Profile */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				CURRENT_PROJECT_VERSION = 1;
				GENERATE_INFOPLIST_FILE = YES;
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.example.fifteen.RunnerTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_VERSION = 5.0;
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/fifteen.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/fifteen";
			};
			name = Profile;
		};
		338D0CE9231458BD00FA5F75 /* Profile */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CODE_SIGN_IDENTITY = "-";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				MACOSX_DEPLOYMENT_TARGET = 10.14;
				MTL_ENABLE_DEBUG_INFO = NO;
				SDKROOT = macosx;
				SWIFT_COMPILATION_MODE = wholemodule;
				SWIFT_OPTIMIZATION_LEVEL = "-O";
			};
			name = Profile;
		};
		338D0CEA231458BD00FA5F75 /* Profile */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CLANG_ENABLE_MODULES = YES;
				CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
				CODE_SIGN_STYLE = Automatic;
				COMBINE_HIDPI_IMAGES = YES;
				INFOPLIST_FILE = Runner/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/../Frameworks",
				);
				PROVISIONING_PROFILE_SPECIFIER = "";
				SWIFT_VERSION = 5.0;
			};
			name = Profile;
		};
		338D0CEB231458BD00FA5F75 /* Profile */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				CODE_SIGN_STYLE = Manual;
				PRODUCT_NAME = "$(TARGET_NAME)";
			};
			name = Profile;
		};
		33CC10F92044A3C60003C045 /* Debug */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CODE_SIGN_IDENTITY = "-";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				MACOSX_DEPLOYMENT_TARGET = 10.14;
				MTL_ENABLE_DEBUG_INFO = YES;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = macosx;
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
			};
			name = Debug;
		};
		33CC10FA2044A3C60003C045 /* Release */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CODE_SIGN_IDENTITY = "-";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				MACOSX_DEPLOYMENT_TARGET = 10.14;
				MTL_ENABLE_DEBUG_INFO = NO;
				SDKROOT = macosx;
				SWIFT_COMPILATION_MODE = wholemodule;
				SWIFT_OPTIMIZATION_LEVEL = "-O";
			};
			name = Release;
		};
		33CC10FC2044A3C60003C045 /* Debug */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CLANG_ENABLE_MODULES = YES;
				CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
				CODE_SIGN_STYLE = Automatic;
				COMBINE_HIDPI_IMAGES = YES;
				INFOPLIST_FILE = Runner/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/../Frameworks",
				);
				PROVISIONING_PROFILE_SPECIFIER = "";
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
				SWIFT_VERSION = 5.0;
			};
			name = Debug;
		};
		33CC10FD2044A3C60003C045 /* Release */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CLANG_ENABLE_MODULES = YES;
				CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
				CODE_SIGN_STYLE = Automatic;
				COMBINE_HIDPI_IMAGES = YES;
				INFOPLIST_FILE = Runner/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/../Frameworks",
				);
				PROVISIONING_PROFILE_SPECIFIER = "";
				SWIFT_VERSION = 5.0;
			};
			name = Release;
		};
		33CC111C2044C6BA0003C045 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				CODE_SIGN_STYLE = Manual;
				PRODUCT_NAME = "$(TARGET_NAME)";
			};
			name = Debug;
		};
		33CC111D2044C6BA0003C045 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				CODE_SIGN_STYLE = Automatic;
				PRODUCT_NAME = "$(TARGET_NAME)";
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				331C80DB294CF71000263BE5 /* Debug */,
				331C80DC294CF71000263BE5 /* Release */,
				331C80DD294CF71000263BE5 /* Profile */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				33CC10F92044A3C60003C045 /* Debug */,
				33CC10FA2044A3C60003C045 /* Release */,
				338D0CE9231458BD00FA5F75 /* Profile */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				33CC10FC2044A3C60003C045 /* Debug */,
				33CC10FD2044A3C60003C045 /* Release */,
				338D0CEA231458BD00FA5F75 /* Profile */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				33CC111C2044C6BA0003C045 /* Debug */,
				33CC111D2044C6BA0003C045 /* Release */,
				338D0CEB231458BD00FA5F75 /* Profile */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */
	};
	rootObject = 33CC10E52044A3C60003C045 /* Project object */;
}
dir Runner.xcworkspace
dir xcshareddata
IDEWorkspaceChecks.plist ASCII text
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>
contents.xcworkspacedata ASCII text
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:Runner.xcodeproj">
   </FileRef>
</Workspace>
dir RunnerTests
RunnerTests.swift ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import FlutterMacOS
import Cocoa
import XCTest

class RunnerTests: XCTestCase {

  func testExample() {
    // If you add code to the Runner application, consider adding tests here.
    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
  }

}
dir web
dir icons
Icon-192.png PNG image data, 192 x 192, 8-bit/color RGBA, non-interlaced
Icon-512.png PNG image data, 512 x 512, 8-bit/color RGBA, non-interlaced
Icon-maskable-192.png PNG image data, 192 x 192, 8-bit/color RGBA, non-interlaced
Icon-maskable-512.png PNG image data, 512 x 512, 8-bit/color RGBA, non-interlaced
favicon.png PNG image data, 16 x 16, 8-bit/color RGBA, non-interlaced
index.html 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
<!DOCTYPE html>
<html>
<head>
  <!--
    If you are serving your web app in a path other than the root, change the
    href value below to reflect the base path you are serving from.

    The path provided below has to start and end with a slash "/" in order for
    it to work correctly.

    For more details:
    * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base

    This is a placeholder for base href that will be replaced by the value of
    the `--base-href` argument provided to `flutter build`.
  -->
  <base href="$FLUTTER_BASE_HREF">

  <meta charset="UTF-8">
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="A new Flutter project.">

  <!-- iOS meta tags & icons -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="15">
  <link rel="apple-touch-icon" href="icons/Icon-192.png">

  <!-- Favicon -->
  <link rel="icon" type="image/png" href="favicon.png"/>

  <title>15</title>
  <link rel="manifest" href="manifest.json">

  <script>
    // The value below is injected by flutter build, do not touch.
    var serviceWorkerVersion = null;
  </script>
  <!-- This script adds the flutter initialization JS code -->
  <script src="flutter.js" defer></script>
</head>
<body>
  <script>
    window.addEventListener('load', function(ev) {
      // Download main.dart.js
      _flutter.loader.loadEntrypoint({
        serviceWorker: {
          serviceWorkerVersion: serviceWorkerVersion,
        },
        onEntrypointLoaded: function(engineInitializer) {
          engineInitializer.initializeEngine().then(function(appRunner) {
            appRunner.runApp();
          });
        }
      });
    });
  </script>
</body>
</html>
manifest.json JSON text data
 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
{
    "name": "15",
    "short_name": "15",
    "start_url": ".",
    "display": "standalone",
    "background_color": "#ffffff",
    "theme_color": "#000000",
    "description": "A new Flutter project.",
    "orientation": "portrait-primary",
    "prefer_related_applications": false,
    "icons": [
        {
            "src": "icons/Icon-192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "icons/Icon-512.png",
            "sizes": "512x512",
            "type": "image/png"
        },
        {
            "src": "icons/Icon-maskable-192.png",
            "sizes": "192x192",
            "type": "image/png",
            "purpose": "maskable"
        },
        {
            "src": "icons/Icon-maskable-512.png",
            "sizes": "512x512",
            "type": "image/png",
            "purpose": "maskable"
        }
    ]
}
dir windows
dir flutter
CMakeLists.txt 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
# This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.14)

set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")

# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)

# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")

# === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")

# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)

list(APPEND FLUTTER_LIBRARY_HEADERS
  "flutter_export.h"
  "flutter_windows.h"
  "flutter_messenger.h"
  "flutter_plugin_registrar.h"
  "flutter_texture_registrar.h"
)
list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
  "${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
add_dependencies(flutter flutter_assemble)

# === Wrapper ===
list(APPEND CPP_WRAPPER_SOURCES_CORE
  "core_implementations.cc"
  "standard_codec.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
  "plugin_registrar.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_APP
  "flutter_engine.cc"
  "flutter_view_controller.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")

# Wrapper sources needed for a plugin.
add_library(flutter_wrapper_plugin STATIC
  ${CPP_WRAPPER_SOURCES_CORE}
  ${CPP_WRAPPER_SOURCES_PLUGIN}
)
apply_standard_settings(flutter_wrapper_plugin)
set_target_properties(flutter_wrapper_plugin PROPERTIES
  POSITION_INDEPENDENT_CODE ON)
set_target_properties(flutter_wrapper_plugin PROPERTIES
  CXX_VISIBILITY_PRESET hidden)
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
target_include_directories(flutter_wrapper_plugin PUBLIC
  "${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_plugin flutter_assemble)

# Wrapper sources needed for the runner.
add_library(flutter_wrapper_app STATIC
  ${CPP_WRAPPER_SOURCES_CORE}
  ${CPP_WRAPPER_SOURCES_APP}
)
apply_standard_settings(flutter_wrapper_app)
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
target_include_directories(flutter_wrapper_app PUBLIC
  "${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_app flutter_assemble)

# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
add_custom_command(
  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
    ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
    ${CPP_WRAPPER_SOURCES_APP}
    ${PHONY_OUTPUT}
  COMMAND ${CMAKE_COMMAND} -E env
    ${FLUTTER_TOOL_ENVIRONMENT}
    "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
      windows-x64 $<CONFIG>
  VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
  "${FLUTTER_LIBRARY}"
  ${FLUTTER_LIBRARY_HEADERS}
  ${CPP_WRAPPER_SOURCES_CORE}
  ${CPP_WRAPPER_SOURCES_PLUGIN}
  ${CPP_WRAPPER_SOURCES_APP}
)
generated_plugin_registrant.cc ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//
//  Generated file. Do not edit.
//

// clang-format off

#include "generated_plugin_registrant.h"

#include <url_launcher_windows/url_launcher_windows.h>

void RegisterPlugins(flutter::PluginRegistry* registry) {
  UrlLauncherWindowsRegisterWithRegistrar(
      registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}
generated_plugin_registrant.h ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
//
//  Generated file. Do not edit.
//

// clang-format off

#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_

#include <flutter/plugin_registry.h>

// Registers Flutter plugins.
void RegisterPlugins(flutter::PluginRegistry* registry);

#endif  // GENERATED_PLUGIN_REGISTRANT_
generated_plugins.cmake 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
#
# Generated file, do not edit.
#

list(APPEND FLUTTER_PLUGIN_LIST
  url_launcher_windows
)

list(APPEND FLUTTER_FFI_PLUGIN_LIST
)

set(PLUGIN_BUNDLED_LIBRARIES)

foreach(plugin ${FLUTTER_PLUGIN_LIST})
  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)

foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)
dir runner
dir resources
app_icon.ico MS Windows icon resource - 1 icon, -128x-128 with PNG image data, 128 x 128, 8-bit/color RGBA, non-interlaced, 32 bits/pixel
1
cg: couldn't decode file contents
CMakeLists.txt 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
cmake_minimum_required(VERSION 3.14)
project(runner LANGUAGES CXX)

# Define the application target. To change its name, change BINARY_NAME in the
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
# work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME} WIN32
  "flutter_window.cpp"
  "main.cpp"
  "utils.cpp"
  "win32_window.cpp"
  "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
  "Runner.rc"
  "runner.exe.manifest"
)

# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})

# Add preprocessor definitions for the build version.
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")

# Disable Windows macros that collide with C++ standard library functions.
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")

# Add dependency libraries and include directories. Add any application-specific
# dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")

# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)
Runner.rc 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
// Microsoft Visual C++ generated resource script.
//
#pragma code_page(65001)
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// English (United States) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_APP_ICON            ICON                    "resources\\app_icon.ico"


/////////////////////////////////////////////////////////////////////////////
//
// Version
//

#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
#else
#define VERSION_AS_NUMBER 1,0,0,0
#endif

#if defined(FLUTTER_VERSION)
#define VERSION_AS_STRING FLUTTER_VERSION
#else
#define VERSION_AS_STRING "1.0.0"
#endif

VS_VERSION_INFO VERSIONINFO
 FILEVERSION VERSION_AS_NUMBER
 PRODUCTVERSION VERSION_AS_NUMBER
 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
 FILEFLAGS VS_FF_DEBUG
#else
 FILEFLAGS 0x0L
#endif
 FILEOS VOS__WINDOWS32
 FILETYPE VFT_APP
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904e4"
        BEGIN
            VALUE "CompanyName", "com.example" "\0"
            VALUE "FileDescription", "15" "\0"
            VALUE "FileVersion", VERSION_AS_STRING "\0"
            VALUE "InternalName", "15" "\0"
            VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0"
            VALUE "OriginalFilename", "fifteen.exe" "\0"
            VALUE "ProductName", "15" "\0"
            VALUE "ProductVersion", VERSION_AS_STRING "\0"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1252
    END
END

#endif    // English (United States) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED
flutter_window.cpp 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
#include "flutter_window.h"

#include <optional>

#include "flutter/generated_plugin_registrant.h"

FlutterWindow::FlutterWindow(const flutter::DartProject& project)
    : project_(project) {}

FlutterWindow::~FlutterWindow() {}

bool FlutterWindow::OnCreate() {
  if (!Win32Window::OnCreate()) {
    return false;
  }

  RECT frame = GetClientArea();

  // The size here must match the window dimensions to avoid unnecessary surface
  // creation / destruction in the startup path.
  flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
      frame.right - frame.left, frame.bottom - frame.top, project_);
  // Ensure that basic setup of the controller was successful.
  if (!flutter_controller_->engine() || !flutter_controller_->view()) {
    return false;
  }
  RegisterPlugins(flutter_controller_->engine());
  SetChildContent(flutter_controller_->view()->GetNativeWindow());

  flutter_controller_->engine()->SetNextFrameCallback([&]() {
    this->Show();
  });

  return true;
}

void FlutterWindow::OnDestroy() {
  if (flutter_controller_) {
    flutter_controller_ = nullptr;
  }

  Win32Window::OnDestroy();
}

LRESULT
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
                              WPARAM const wparam,
                              LPARAM const lparam) noexcept {
  // Give Flutter, including plugins, an opportunity to handle window messages.
  if (flutter_controller_) {
    std::optional<LRESULT> result =
        flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
                                                      lparam);
    if (result) {
      return *result;
    }
  }

  switch (message) {
    case WM_FONTCHANGE:
      flutter_controller_->engine()->ReloadSystemFonts();
      break;
  }

  return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
}
flutter_window.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
#ifndef RUNNER_FLUTTER_WINDOW_H_
#define RUNNER_FLUTTER_WINDOW_H_

#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>

#include <memory>

#include "win32_window.h"

// A window that does nothing but host a Flutter view.
class FlutterWindow : public Win32Window {
 public:
  // Creates a new FlutterWindow hosting a Flutter view running |project|.
  explicit FlutterWindow(const flutter::DartProject& project);
  virtual ~FlutterWindow();

 protected:
  // Win32Window:
  bool OnCreate() override;
  void OnDestroy() override;
  LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
                         LPARAM const lparam) noexcept override;

 private:
  // The project to run.
  flutter::DartProject project_;

  // The Flutter instance hosted by this window.
  std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
};

#endif  // RUNNER_FLUTTER_WINDOW_H_
main.cpp 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
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>

#include "flutter_window.h"
#include "utils.h"

int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
                      _In_ wchar_t *command_line, _In_ int show_command) {
  // Attach to console when present (e.g., 'flutter run') or create a
  // new console when running with a debugger.
  if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
    CreateAndAttachConsole();
  }

  // Initialize COM, so that it is available for use in the library and/or
  // plugins.
  ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);

  flutter::DartProject project(L"data");

  std::vector<std::string> command_line_arguments =
      GetCommandLineArguments();

  project.set_dart_entrypoint_arguments(std::move(command_line_arguments));

  FlutterWindow window(project);
  Win32Window::Point origin(10, 10);
  Win32Window::Size size(405, 720);
  if (!window.Create(L"15", origin, size)) {
    return EXIT_FAILURE;
  }
  window.SetQuitOnClose(true);

  ::MSG msg;
  while (::GetMessage(&msg, nullptr, 0, 0)) {
    ::TranslateMessage(&msg);
    ::DispatchMessage(&msg);
  }

  ::CoUninitialize();
  return EXIT_SUCCESS;
}
resource.h ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Runner.rc
//
#define IDI_APP_ICON                    101

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif
runner.exe.manifest ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
    </windowsSettings>
  </application>
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!-- Windows 10 and Windows 11 -->
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
      <!-- Windows 8.1 -->
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
      <!-- Windows 8 -->
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
      <!-- Windows 7 -->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
    </application>
  </compatibility>
</assembly>
utils.cpp 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
#include "utils.h"

#include <flutter_windows.h>
#include <io.h>
#include <stdio.h>
#include <windows.h>

#include <iostream>

void CreateAndAttachConsole() {
  if (::AllocConsole()) {
    FILE *unused;
    if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
      _dup2(_fileno(stdout), 1);
    }
    if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
      _dup2(_fileno(stdout), 2);
    }
    std::ios::sync_with_stdio();
    FlutterDesktopResyncOutputStreams();
  }
}

std::vector<std::string> GetCommandLineArguments() {
  // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
  int argc;
  wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
  if (argv == nullptr) {
    return std::vector<std::string>();
  }

  std::vector<std::string> command_line_arguments;

  // Skip the first argument as it's the binary name.
  for (int i = 1; i < argc; i++) {
    command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
  }

  ::LocalFree(argv);

  return command_line_arguments;
}

std::string Utf8FromUtf16(const wchar_t* utf16_string) {
  if (utf16_string == nullptr) {
    return std::string();
  }
  int target_length = ::WideCharToMultiByte(
      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
      -1, nullptr, 0, nullptr, nullptr)
    -1; // remove the trailing null character
  int input_length = (int)wcslen(utf16_string);
  std::string utf8_string;
  if (target_length <= 0 || target_length > utf8_string.max_size()) {
    return utf8_string;
  }
  utf8_string.resize(target_length);
  int converted_length = ::WideCharToMultiByte(
      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
      input_length, utf8_string.data(), target_length, nullptr, nullptr);
  if (converted_length == 0) {
    return std::string();
  }
  return utf8_string;
}
utils.h ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#ifndef RUNNER_UTILS_H_
#define RUNNER_UTILS_H_

#include <string>
#include <vector>

// Creates a console for the process, and redirects stdout and stderr to
// it for both the runner and the Flutter library.
void CreateAndAttachConsole();

// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
// encoded in UTF-8. Returns an empty std::string on failure.
std::string Utf8FromUtf16(const wchar_t* utf16_string);

// Gets the command line arguments passed in as a std::vector<std::string>,
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
std::vector<std::string> GetCommandLineArguments();

#endif  // RUNNER_UTILS_H_
win32_window.cpp 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
#include "win32_window.h"

#include <dwmapi.h>
#include <flutter_windows.h>

#include "resource.h"

namespace {

/// Window attribute that enables dark mode window decorations.
///
/// Redefined in case the developer's machine has a Windows SDK older than
/// version 10.0.22000.0.
/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif

constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";

/// Registry key for app theme preference.
///
/// A value of 0 indicates apps should use dark mode. A non-zero or missing
/// value indicates apps should use light mode.
constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
  L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";

// The number of Win32Window objects that currently exist.
static int g_active_window_count = 0;

using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);

// Scale helper to convert logical scaler values to physical using passed in
// scale factor
int Scale(int source, double scale_factor) {
  return static_cast<int>(source * scale_factor);
}

// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
// This API is only needed for PerMonitor V1 awareness mode.
void EnableFullDpiSupportIfAvailable(HWND hwnd) {
  HMODULE user32_module = LoadLibraryA("User32.dll");
  if (!user32_module) {
    return;
  }
  auto enable_non_client_dpi_scaling =
      reinterpret_cast<EnableNonClientDpiScaling*>(
          GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
  if (enable_non_client_dpi_scaling != nullptr) {
    enable_non_client_dpi_scaling(hwnd);
  }
  FreeLibrary(user32_module);
}

}  // namespace

// Manages the Win32Window's window class registration.
class WindowClassRegistrar {
 public:
  ~WindowClassRegistrar() = default;

  // Returns the singleton registrar instance.
  static WindowClassRegistrar* GetInstance() {
    if (!instance_) {
      instance_ = new WindowClassRegistrar();
    }
    return instance_;
  }

  // Returns the name of the window class, registering the class if it hasn't
  // previously been registered.
  const wchar_t* GetWindowClass();

  // Unregisters the window class. Should only be called if there are no
  // instances of the window.
  void UnregisterWindowClass();

 private:
  WindowClassRegistrar() = default;

  static WindowClassRegistrar* instance_;

  bool class_registered_ = false;
};

WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;

const wchar_t* WindowClassRegistrar::GetWindowClass() {
  if (!class_registered_) {
    WNDCLASS window_class{};
    window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
    window_class.lpszClassName = kWindowClassName;
    window_class.style = CS_HREDRAW | CS_VREDRAW;
    window_class.cbClsExtra = 0;
    window_class.cbWndExtra = 0;
    window_class.hInstance = GetModuleHandle(nullptr);
    window_class.hIcon =
        LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
    window_class.hbrBackground = 0;
    window_class.lpszMenuName = nullptr;
    window_class.lpfnWndProc = Win32Window::WndProc;
    RegisterClass(&window_class);
    class_registered_ = true;
  }
  return kWindowClassName;
}

void WindowClassRegistrar::UnregisterWindowClass() {
  UnregisterClass(kWindowClassName, nullptr);
  class_registered_ = false;
}

Win32Window::Win32Window() {
  ++g_active_window_count;
}

Win32Window::~Win32Window() {
  --g_active_window_count;
  Destroy();
}

bool Win32Window::Create(const std::wstring& title,
                         const Point& origin,
                         const Size& size) {
  Destroy();

  const wchar_t* window_class =
      WindowClassRegistrar::GetInstance()->GetWindowClass();

  const POINT target_point = {static_cast<LONG>(origin.x),
                              static_cast<LONG>(origin.y)};
  HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
  UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
  double scale_factor = dpi / 96.0;

  HWND window = CreateWindow(
      window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
      Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
      Scale(size.width, scale_factor), Scale(size.height, scale_factor),
      nullptr, nullptr, GetModuleHandle(nullptr), this);

  if (!window) {
    return false;
  }

  UpdateTheme(window);

  return OnCreate();
}

bool Win32Window::Show() {
  return ShowWindow(window_handle_, SW_SHOWNORMAL);
}

// static
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
                                      UINT const message,
                                      WPARAM const wparam,
                                      LPARAM const lparam) noexcept {
  if (message == WM_NCCREATE) {
    auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
    SetWindowLongPtr(window, GWLP_USERDATA,
                     reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));

    auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
    EnableFullDpiSupportIfAvailable(window);
    that->window_handle_ = window;
  } else if (Win32Window* that = GetThisFromHandle(window)) {
    return that->MessageHandler(window, message, wparam, lparam);
  }

  return DefWindowProc(window, message, wparam, lparam);
}

LRESULT
Win32Window::MessageHandler(HWND hwnd,
                            UINT const message,
                            WPARAM const wparam,
                            LPARAM const lparam) noexcept {
  switch (message) {
    case WM_DESTROY:
      window_handle_ = nullptr;
      Destroy();
      if (quit_on_close_) {
        PostQuitMessage(0);
      }
      return 0;

    case WM_DPICHANGED: {
      auto newRectSize = reinterpret_cast<RECT*>(lparam);
      LONG newWidth = newRectSize->right - newRectSize->left;
      LONG newHeight = newRectSize->bottom - newRectSize->top;

      SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
                   newHeight, SWP_NOZORDER | SWP_NOACTIVATE);

      return 0;
    }
    case WM_SIZE: {
      RECT rect = GetClientArea();
      if (child_content_ != nullptr) {
        // Size and position the child window.
        MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
                   rect.bottom - rect.top, TRUE);
      }
      return 0;
    }

    case WM_ACTIVATE:
      if (child_content_ != nullptr) {
        SetFocus(child_content_);
      }
      return 0;

    case WM_DWMCOLORIZATIONCOLORCHANGED:
      UpdateTheme(hwnd);
      return 0;
  }

  return DefWindowProc(window_handle_, message, wparam, lparam);
}

void Win32Window::Destroy() {
  OnDestroy();

  if (window_handle_) {
    DestroyWindow(window_handle_);
    window_handle_ = nullptr;
  }
  if (g_active_window_count == 0) {
    WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
  }
}

Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
  return reinterpret_cast<Win32Window*>(
      GetWindowLongPtr(window, GWLP_USERDATA));
}

void Win32Window::SetChildContent(HWND content) {
  child_content_ = content;
  SetParent(content, window_handle_);
  RECT frame = GetClientArea();

  MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
             frame.bottom - frame.top, true);

  SetFocus(child_content_);
}

RECT Win32Window::GetClientArea() {
  RECT frame;
  GetClientRect(window_handle_, &frame);
  return frame;
}

HWND Win32Window::GetHandle() {
  return window_handle_;
}

void Win32Window::SetQuitOnClose(bool quit_on_close) {
  quit_on_close_ = quit_on_close;
}

bool Win32Window::OnCreate() {
  // No-op; provided for subclasses.
  return true;
}

void Win32Window::OnDestroy() {
  // No-op; provided for subclasses.
}

void Win32Window::UpdateTheme(HWND const window) {
  DWORD light_mode;
  DWORD light_mode_size = sizeof(light_mode);
  LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
                               kGetPreferredBrightnessRegValue,
                               RRF_RT_REG_DWORD, nullptr, &light_mode,
                               &light_mode_size);

  if (result == ERROR_SUCCESS) {
    BOOL enable_dark_mode = light_mode == 0;
    DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
                          &enable_dark_mode, sizeof(enable_dark_mode));
  }
}
win32_window.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
#ifndef RUNNER_WIN32_WINDOW_H_
#define RUNNER_WIN32_WINDOW_H_

#include <windows.h>

#include <functional>
#include <memory>
#include <string>

// A class abstraction for a high DPI-aware Win32 Window. Intended to be
// inherited from by classes that wish to specialize with custom
// rendering and input handling
class Win32Window {
 public:
  struct Point {
    unsigned int x;
    unsigned int y;
    Point(unsigned int x, unsigned int y) : x(x), y(y) {}
  };

  struct Size {
    unsigned int width;
    unsigned int height;
    Size(unsigned int width, unsigned int height)
        : width(width), height(height) {}
  };

  Win32Window();
  virtual ~Win32Window();

  // Creates a win32 window with |title| that is positioned and sized using
  // |origin| and |size|. New windows are created on the default monitor. Window
  // sizes are specified to the OS in physical pixels, hence to ensure a
  // consistent size this function will scale the inputted width and height as
  // as appropriate for the default monitor. The window is invisible until
  // |Show| is called. Returns true if the window was created successfully.
  bool Create(const std::wstring& title, const Point& origin, const Size& size);

  // Show the current window. Returns true if the window was successfully shown.
  bool Show();

  // Release OS resources associated with window.
  void Destroy();

  // Inserts |content| into the window tree.
  void SetChildContent(HWND content);

  // Returns the backing Window handle to enable clients to set icon and other
  // window properties. Returns nullptr if the window has been destroyed.
  HWND GetHandle();

  // If true, closing this window will quit the application.
  void SetQuitOnClose(bool quit_on_close);

  // Return a RECT representing the bounds of the current client area.
  RECT GetClientArea();

 protected:
  // Processes and route salient window messages for mouse handling,
  // size change and DPI. Delegates handling of these to member overloads that
  // inheriting classes can handle.
  virtual LRESULT MessageHandler(HWND window,
                                 UINT const message,
                                 WPARAM const wparam,
                                 LPARAM const lparam) noexcept;

  // Called when CreateAndShow is called, allowing subclass window-related
  // setup. Subclasses should return false if setup fails.
  virtual bool OnCreate();

  // Called when Destroy is called.
  virtual void OnDestroy();

 private:
  friend class WindowClassRegistrar;

  // OS callback called by message pump. Handles the WM_NCCREATE message which
  // is passed when the non-client area is being created and enables automatic
  // non-client DPI scaling so that the non-client area automatically
  // responds to changes in DPI. All other messages are handled by
  // MessageHandler.
  static LRESULT CALLBACK WndProc(HWND const window,
                                  UINT const message,
                                  WPARAM const wparam,
                                  LPARAM const lparam) noexcept;

  // Retrieves a class instance pointer for |window|
  static Win32Window* GetThisFromHandle(HWND const window) noexcept;

  // Update the window frame's theme to match the system theme.
  static void UpdateTheme(HWND const window);

  bool quit_on_close_ = false;

  // window handle for top level window.
  HWND window_handle_ = nullptr;

  // window handle for hosted content.
  HWND child_content_ = nullptr;
};

#endif  // RUNNER_WIN32_WINDOW_H_
CMakeLists.txt 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
# Project-level configuration.
cmake_minimum_required(VERSION 3.14)
project(fifteen LANGUAGES CXX)

# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "fifteen")

# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)

# Define build configuration option.
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTICONFIG)
  set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
    CACHE STRING "" FORCE)
else()
  if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    set(CMAKE_BUILD_TYPE "Debug" CACHE
      STRING "Flutter build mode" FORCE)
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
      "Debug" "Profile" "Release")
  endif()
endif()
# Define settings for the Profile build mode.
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")

# Use Unicode for all projects.
add_definitions(-DUNICODE -D_UNICODE)

# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
  target_compile_features(${TARGET} PUBLIC cxx_std_17)
  target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
  target_compile_options(${TARGET} PRIVATE /EHsc)
  target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
  target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
endfunction()

# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})

# Application build; see runner/CMakeLists.txt.
add_subdirectory("runner")


# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)


# === Installation ===
# Support files are copied into place next to the executable, so that it can
# run in place. This is done instead of making a separate bundle (as on Linux)
# so that building and running from within Visual Studio will work.
set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
# Make the "install" step default, as it's required to run.
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
  set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()

set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")

install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
  COMPONENT Runtime)

install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
  COMPONENT Runtime)

install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
  COMPONENT Runtime)

if(PLUGIN_BUNDLED_LIBRARIES)
  install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
    DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
    COMPONENT Runtime)
endif()

# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
  file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
  " COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
  DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)

# Install the AOT library on non-Debug builds only.
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
  CONFIGURATIONS Profile;Release
  COMPONENT Runtime)
LICENSE.txt 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
MIT License

Copyright (c) 2023 Lars Müller

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
README.md ASCII text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 15

The [15](https://en.wikipedia.org/wiki/15_puzzle) puzzle.

## App

Allows creating, playing & editing puzzles. Includes a solver. Puzzles can be copied to and pasted from clipboard in hex format.
You can click on the neighboring buttons of the blank spots or use WASD or slide gestures to play.

Web (JS) build won't work since the board representation requires 64-bit ints;
might work with the experimental WASM build mode.

## CLI

A simple CLI for the solver is at `cli_solve.dart`.

Run using `dart run cli_solve.dart <hex board>`,
compile using `dart compile exe cli_solve.dart -o solve` (and then run using `./solve <hex board>`).

The output is a sequence of puzzle states, from the given problem to the winning board, as hex, with newlines inserted for better readability.

The hex format is simply a hex string of the hex digits of the 16 tiles; the blank tile is assigned 0. The winning board is stored as `123456789abcdef0` using this format. Whitespace is not allowed.
analysis_options.yaml 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
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml

linter:
  # The lint rules applied to this project can be customized in the
  # section below to disable rules from the `package:flutter_lints/flutter.yaml`
  # included above or to enable additional rules. A list of all available lints
  # and their documentation is published at
  # https://dart-lang.github.io/linter/lints/index.html.
  #
  # Instead of disabling a lint rule for the entire project in the
  # section below, it can also be suppressed for a single line of code
  # or a specific dart file by using the `// ignore: name_of_lint` and
  # `// ignore_for_file: name_of_lint` syntax on the line or in the file
  # producing the lint.
  rules:
    # avoid_print: false  # Uncomment to disable the `avoid_print` rule
    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
cli_solve.dart 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
import 'dart:io';

import 'lib/board.dart' as brd;

typedef Board = brd.Board;

// ignore_for_file: avoid_print

usage() {
  print("arg: <hex board>");
  exit(1);
}

void main(List<String> args) {
  if (args.length != 1) usage();
  Board? board = brd.fromHex(args[0]);
  if (board == null) usage();
  if (!brd.isSolvable(board!)) {
    print("unsolvable");
    exit(1);
  }
  final linefeed = Platform.isWindows ? "\r\n" : "\n";
  print([board]
      .followedBy(brd.solve(board))
      .map((b) => brd.toHex(b, linefeed))
      .join(linefeed + linefeed));
}
pubspec.lock 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
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
  archive:
    dependency: transitive
    description:
      name: archive
      sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
      url: "https://pub.dev"
    source: hosted
    version: "3.3.7"
  args:
    dependency: transitive
    description:
      name: args
      sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
      url: "https://pub.dev"
    source: hosted
    version: "2.4.2"
  async:
    dependency: transitive
    description:
      name: async
      sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
      url: "https://pub.dev"
    source: hosted
    version: "2.11.0"
  boolean_selector:
    dependency: transitive
    description:
      name: boolean_selector
      sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
      url: "https://pub.dev"
    source: hosted
    version: "2.1.1"
  characters:
    dependency: transitive
    description:
      name: characters
      sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
      url: "https://pub.dev"
    source: hosted
    version: "1.3.0"
  checked_yaml:
    dependency: transitive
    description:
      name: checked_yaml
      sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
      url: "https://pub.dev"
    source: hosted
    version: "2.0.3"
  cli_util:
    dependency: transitive
    description:
      name: cli_util
      sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7
      url: "https://pub.dev"
    source: hosted
    version: "0.4.0"
  clock:
    dependency: transitive
    description:
      name: clock
      sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
      url: "https://pub.dev"
    source: hosted
    version: "1.1.1"
  collection:
    dependency: transitive
    description:
      name: collection
      sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
      url: "https://pub.dev"
    source: hosted
    version: "1.17.1"
  convert:
    dependency: transitive
    description:
      name: convert
      sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
      url: "https://pub.dev"
    source: hosted
    version: "3.1.1"
  crypto:
    dependency: transitive
    description:
      name: crypto
      sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
      url: "https://pub.dev"
    source: hosted
    version: "3.0.3"
  fake_async:
    dependency: transitive
    description:
      name: fake_async
      sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
      url: "https://pub.dev"
    source: hosted
    version: "1.3.1"
  ffi:
    dependency: transitive
    description:
      name: ffi
      sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
      url: "https://pub.dev"
    source: hosted
    version: "2.0.2"
  file:
    dependency: transitive
    description:
      name: file
      sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
      url: "https://pub.dev"
    source: hosted
    version: "6.1.4"
  flutter:
    dependency: "direct main"
    description: flutter
    source: sdk
    version: "0.0.0"
  flutter_launcher_icons:
    dependency: "direct dev"
    description:
      name: flutter_launcher_icons
      sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
      url: "https://pub.dev"
    source: hosted
    version: "0.13.1"
  flutter_lints:
    dependency: "direct dev"
    description:
      name: flutter_lints
      sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4"
      url: "https://pub.dev"
    source: hosted
    version: "2.0.2"
  flutter_test:
    dependency: "direct dev"
    description: flutter
    source: sdk
    version: "0.0.0"
  flutter_web_plugins:
    dependency: transitive
    description: flutter
    source: sdk
    version: "0.0.0"
  image:
    dependency: transitive
    description:
      name: image
      sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf
      url: "https://pub.dev"
    source: hosted
    version: "4.0.17"
  js:
    dependency: transitive
    description:
      name: js
      sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
      url: "https://pub.dev"
    source: hosted
    version: "0.6.7"
  json_annotation:
    dependency: transitive
    description:
      name: json_annotation
      sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
      url: "https://pub.dev"
    source: hosted
    version: "4.8.1"
  lints:
    dependency: transitive
    description:
      name: lints
      sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
      url: "https://pub.dev"
    source: hosted
    version: "2.1.1"
  matcher:
    dependency: transitive
    description:
      name: matcher
      sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
      url: "https://pub.dev"
    source: hosted
    version: "0.12.15"
  material_color_utilities:
    dependency: transitive
    description:
      name: material_color_utilities
      sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
      url: "https://pub.dev"
    source: hosted
    version: "0.2.0"
  meta:
    dependency: transitive
    description:
      name: meta
      sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
      url: "https://pub.dev"
    source: hosted
    version: "1.9.1"
  path:
    dependency: transitive
    description:
      name: path
      sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
      url: "https://pub.dev"
    source: hosted
    version: "1.8.3"
  path_provider_linux:
    dependency: transitive
    description:
      name: path_provider_linux
      sha256: ba2b77f0c52a33db09fc8caf85b12df691bf28d983e84cf87ff6d693cfa007b3
      url: "https://pub.dev"
    source: hosted
    version: "2.2.0"
  path_provider_platform_interface:
    dependency: transitive
    description:
      name: path_provider_platform_interface
      sha256: bced5679c7df11190e1ddc35f3222c858f328fff85c3942e46e7f5589bf9eb84
      url: "https://pub.dev"
    source: hosted
    version: "2.1.0"
  path_provider_windows:
    dependency: transitive
    description:
      name: path_provider_windows
      sha256: ee0e0d164516b90ae1f970bdf29f726f1aa730d7cfc449ecc74c495378b705da
      url: "https://pub.dev"
    source: hosted
    version: "2.2.0"
  petitparser:
    dependency: transitive
    description:
      name: petitparser
      sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
      url: "https://pub.dev"
    source: hosted
    version: "5.4.0"
  platform:
    dependency: transitive
    description:
      name: platform
      sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
      url: "https://pub.dev"
    source: hosted
    version: "3.1.0"
  plugin_platform_interface:
    dependency: transitive
    description:
      name: plugin_platform_interface
      sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd"
      url: "https://pub.dev"
    source: hosted
    version: "2.1.5"
  pointycastle:
    dependency: transitive
    description:
      name: pointycastle
      sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
      url: "https://pub.dev"
    source: hosted
    version: "3.7.3"
  shared_preferences:
    dependency: "direct main"
    description:
      name: shared_preferences
      sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1"
      url: "https://pub.dev"
    source: hosted
    version: "2.2.0"
  shared_preferences_android:
    dependency: transitive
    description:
      name: shared_preferences_android
      sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076
      url: "https://pub.dev"
    source: hosted
    version: "2.2.0"
  shared_preferences_foundation:
    dependency: transitive
    description:
      name: shared_preferences_foundation
      sha256: f39696b83e844923b642ce9dd4bd31736c17e697f6731a5adf445b1274cf3cd4
      url: "https://pub.dev"
    source: hosted
    version: "2.3.2"
  shared_preferences_linux:
    dependency: transitive
    description:
      name: shared_preferences_linux
      sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1"
      url: "https://pub.dev"
    source: hosted
    version: "2.3.0"
  shared_preferences_platform_interface:
    dependency: transitive
    description:
      name: shared_preferences_platform_interface
      sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1"
      url: "https://pub.dev"
    source: hosted
    version: "2.3.0"
  shared_preferences_web:
    dependency: transitive
    description:
      name: shared_preferences_web
      sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a"
      url: "https://pub.dev"
    source: hosted
    version: "2.2.0"
  shared_preferences_windows:
    dependency: transitive
    description:
      name: shared_preferences_windows
      sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d
      url: "https://pub.dev"
    source: hosted
    version: "2.3.0"
  sky_engine:
    dependency: transitive
    description: flutter
    source: sdk
    version: "0.0.99"
  source_span:
    dependency: transitive
    description:
      name: source_span
      sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
      url: "https://pub.dev"
    source: hosted
    version: "1.9.1"
  stack_trace:
    dependency: transitive
    description:
      name: stack_trace
      sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
      url: "https://pub.dev"
    source: hosted
    version: "1.11.0"
  stream_channel:
    dependency: transitive
    description:
      name: stream_channel
      sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
      url: "https://pub.dev"
    source: hosted
    version: "2.1.1"
  string_scanner:
    dependency: transitive
    description:
      name: string_scanner
      sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
      url: "https://pub.dev"
    source: hosted
    version: "1.2.0"
  term_glyph:
    dependency: transitive
    description:
      name: term_glyph
      sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
      url: "https://pub.dev"
    source: hosted
    version: "1.2.1"
  test_api:
    dependency: transitive
    description:
      name: test_api
      sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
      url: "https://pub.dev"
    source: hosted
    version: "0.5.1"
  typed_data:
    dependency: transitive
    description:
      name: typed_data
      sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
      url: "https://pub.dev"
    source: hosted
    version: "1.3.2"
  url_launcher:
    dependency: "direct main"
    description:
      name: url_launcher
      sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e"
      url: "https://pub.dev"
    source: hosted
    version: "6.1.12"
  url_launcher_android:
    dependency: transitive
    description:
      name: url_launcher_android
      sha256: "78cb6dea3e93148615109e58e42c35d1ffbf5ef66c44add673d0ab75f12ff3af"
      url: "https://pub.dev"
    source: hosted
    version: "6.0.37"
  url_launcher_ios:
    dependency: transitive
    description:
      name: url_launcher_ios
      sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2"
      url: "https://pub.dev"
    source: hosted
    version: "6.1.4"
  url_launcher_linux:
    dependency: transitive
    description:
      name: url_launcher_linux
      sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5"
      url: "https://pub.dev"
    source: hosted
    version: "3.0.5"
  url_launcher_macos:
    dependency: transitive
    description:
      name: url_launcher_macos
      sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1"
      url: "https://pub.dev"
    source: hosted
    version: "3.0.6"
  url_launcher_platform_interface:
    dependency: transitive
    description:
      name: url_launcher_platform_interface
      sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea
      url: "https://pub.dev"
    source: hosted
    version: "2.1.3"
  url_launcher_web:
    dependency: transitive
    description:
      name: url_launcher_web
      sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4
      url: "https://pub.dev"
    source: hosted
    version: "2.0.18"
  url_launcher_windows:
    dependency: transitive
    description:
      name: url_launcher_windows
      sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422"
      url: "https://pub.dev"
    source: hosted
    version: "3.0.7"
  vector_math:
    dependency: transitive
    description:
      name: vector_math
      sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
      url: "https://pub.dev"
    source: hosted
    version: "2.1.4"
  win32:
    dependency: transitive
    description:
      name: win32
      sha256: f2add6fa510d3ae152903412227bda57d0d5a8da61d2c39c1fb022c9429a41c0
      url: "https://pub.dev"
    source: hosted
    version: "5.0.6"
  xdg_directories:
    dependency: transitive
    description:
      name: xdg_directories
      sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff
      url: "https://pub.dev"
    source: hosted
    version: "1.0.1"
  xml:
    dependency: transitive
    description:
      name: xml
      sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
      url: "https://pub.dev"
    source: hosted
    version: "6.3.0"
  yaml:
    dependency: transitive
    description:
      name: yaml
      sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
      url: "https://pub.dev"
    source: hosted
    version: "3.1.2"
sdks:
  dart: ">=3.0.6 <4.0.0"
  flutter: ">=3.10.0"
pubspec.yaml 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
name: "fifteen"
description: The 15 puzzle game.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1

environment:
  sdk: '>=3.0.6 <4.0.0'

dependencies:
  flutter:
    sdk: flutter

  shared_preferences: ^2.2.0
  url_launcher: ^6.1.12

dev_dependencies:
  flutter_test:
    sdk: flutter

  flutter_lints: ^2.0.0

  flutter_launcher_icons: "^0.13.1"


flutter_launcher_icons:
  android: "launcher_icon"
  ios: true
  image_path: "assets/icon.png"
  min_sdk_android: 16
  web:
    generate: true
    image_path: "assets/icon.png"
    background_color: "#ffffff"
    theme_color: "#000000"
  windows:
    generate: true
    image_path: "assets/icon.png"
    icon_size: 128
  macos:
    generate: true
    image_path: "assets/icon.png"

round #39

submitted at
0 likes

guesses
comments 0

post a comment


cg39.scm 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
; Brain-Flak implementation in Chicken Scheme for Code Guessing Round 39
; Supports line comments using `#`, ignores invalid characters
; Compilation & invocation: `csc cg39.scm && ./cg39 FILE [ARGS ...]`
(import (chicken process-context))
(define (parse port)
  (define (paren closing)
    (read-char port)
    (let ((sub (parse port)))
      (assert (eqv? (read-char port) closing))
      (cons
       (cons closing sub)
       (parse port))))
  (let ((c (peek-char port)))
    (case c
      ((#\) #\] #\} #\> #!eof) '())
      ((#\() (paren #\)))
      ((#\[) (paren #\]))
      ((#\{) (paren #\}))
      ((#\<) (paren #\>))
      (else
       (if (eqv? (read-char port) #\#)
           (do () ((memv (read-char port) '(#\newline #!eof)))))
       (parse port)))))
(define (exec exp active)
  (let ((inactive '()))
    (define (pop)
      (if (null? active) 0
        (let ((top (car active))) (set! active (cdr active)) top)))
    (define (push x)
      (set! active (cons x active)))
    (define (toggle)
      (let ((a active)) (set! active inactive) (set! inactive a)))
    (define (ctx-eval exp)
      (if (null? exp) 0
          (let ((sub (cdr exp)) (nilad (null? (cdr exp))))
            (if (list? (car exp))
                (apply + (map ctx-eval exp))
                (case (car exp)
                  ((#\)) (if nilad 1 (let ((k (ctx-eval sub))) (push k) k)))
                  ((#\]) (if nilad (length active) (- (ctx-eval sub))))
                  ((#\}) (if nilad (pop)
                       (do ((sum 0))
                           ((or (null? active) (= (car active) 0)) sum)
                         (set! sum (+ sum (ctx-eval sub))))))
                  ((#\>) (begin (if nilad (toggle) (ctx-eval sub)) 0)))))))
    (ctx-eval exp))
  (for-each (lambda (x) (display x) (newline)) active))
(let ((args (command-line-arguments)))
  (call-with-input-file (car args)
    (lambda (port)
      (let ((exp (parse port)))
        (assert (eof-object? (read-char port)))
        (exec exp (map string->number (reverse (cdr args))))))))

round #38

submitted at
0 likes

guesses
comments 0

post a comment


38.go 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
package main

import (
	"os"
	"fmt"
	"bufio"
)

func pop[T any](slice []T) ([]T, T) {
	return slice[:len(slice)-1], slice[len(slice) - 1]
}

func apply(op byte, a, b int32) int32 {
	switch op {
		case '+': return a + b
		case '-': return a - b
		case '*': return a * b
		case '/': return a / b
	}
	panic("invalid op")
}

// 1*1 + 2*2
// 1*1  +  2    *    3
// 3  -  2 - 1-1
func main() {
	var operands []int32
	var ops []byte
	var precedences []uint // higher is lower
	var eval func() int32
	eval = func() int32 {
		if len(operands) == 0 {
			panic("no operands")
		}
		if len(ops) == len(operands) {
			panic("too few operands")
		}
		for {
			if len(ops) == 0 {
				res := operands[0]
				operands = nil
				return res
			}
			var topOp byte
			var topPrec uint
			var lhs, rhs int32
			ops, topOp = pop(ops)
			precedences, topPrec = pop(precedences)
			operands, rhs = pop(operands)
			if len(ops) > 0 {
				botPrec := precedences[len(precedences) - 1]
				if botPrec <= topPrec {
					return apply(topOp, eval(), rhs)
				}
			}
			operands, lhs = pop(operands)
			operands = append(operands, apply(topOp, lhs, rhs))
		}
	}
	stdin := bufio.NewReader(os.Stdin)
	countSpaces := func() (spaces uint) {
		for {
			c, _ := stdin.ReadByte()
			if c != ' ' {
				break
			}
			spaces++
		}
		stdin.UnreadByte()
		return
	}
	for {
		leadingSpaces := countSpaces()
		c, err := stdin.ReadByte()
		if c == '\n' || err != nil {
			fmt.Println(eval())
		}
		if err != nil {
			return
		}
		if c >= '0' && c <= '9' {
			if len(operands) > len(ops) {
				panic("too many operands")
			}
			operand := int32(c - '0')
			for {
				c, _ := stdin.ReadByte()
				if c < '0' || c > '9' {
					break
				}
				operand = 10 * operand + int32(c - '0')
			}
			stdin.UnreadByte()
			operands = append(operands, operand)
		} else if c == '+' || c == '-' || c == '*' || c == '/' {
			if len(operands) < len(ops) {
				panic("no operand")
			}
			trailingSpaces := countSpaces()
			var precedence uint
			if trailingSpaces > leadingSpaces {
				precedence = trailingSpaces
			} else {
				precedence = leadingSpaces
			}
			precedences = append(precedences, precedence)
			ops = append(ops, c)
		}
	}
}