Compare commits

..

24 Commits

Author SHA1 Message Date
Connor Olding d261b299db move files 2019-03-28 16:45:40 +01:00
Connor Olding 52956fd397 Merge remote-tracking branch 'coin_or_build/master' into pensive 2019-03-28 16:45:40 +01:00
Connor Olding 83b73c0930 move files 2019-03-11 06:50:29 +01:00
Connor Olding f94f5382de Merge remote-tracking branch 'rng64/master' 2019-03-11 06:50:29 +01:00
Connor Olding bddb5c32ea move files 2019-03-11 06:50:28 +01:00
Connor Olding 8ce28c4bf4 Merge remote-tracking branch 'response/master' 2019-03-11 06:50:28 +01:00
Connor Olding 6e1544f860 move files 2019-03-11 06:50:28 +01:00
Connor Olding df441b445c Merge remote-tracking branch 'resnet/master' 2019-03-11 06:50:28 +01:00
Connor Olding 9f94bd2f45 move files 2019-03-11 06:50:27 +01:00
Connor Olding b3d8d55997 Merge remote-tracking branch 'gs/master' 2019-03-11 06:50:27 +01:00
Connor Olding 7188ba47c5 move files 2019-03-11 06:50:27 +01:00
Connor Olding 7739895224 Merge remote-tracking branch 'debug_saves/master' 2019-03-11 06:50:26 +01:00
Connor Olding 3aaee2c09f move files 2019-03-11 06:50:26 +01:00
Connor Olding 61188f00da Merge remote-tracking branch 'atttt/master' 2019-03-11 06:50:26 +01:00
Connor Olding f7e3cfceb1 . 2017-01-05 04:56:35 -08:00
Connor Olding 15d053789e . 2017-01-05 04:55:10 -08:00
Connor Olding e02bca097c . 2016-08-09 01:11:05 -07:00
Connor b61ecf090a 2016-08-08 23:57:20 -07:00
Connor Olding b028ee53d9 . 2016-05-25 11:38:44 -07:00
Connor Olding e61a32c615 . 2016-05-25 11:30:07 -07:00
Connor Olding eeb5d2941e . 2016-05-25 07:31:48 -07:00
Connor Olding 28edd29072 . 2016-05-24 20:15:26 -07:00
Connor db3171ac29 2016-05-24 20:10:24 -07:00
Connor 14af124e09 2016-04-02 08:45:27 -07:00
15 changed files with 1776 additions and 150 deletions

View File

@ -44,11 +44,6 @@ i recently repurposed this script
to render characters to bitmaps for use as textures in games,
but i haven't uploaded that version yet.
### coin\_or\_build
a shell script that builds Bonmin and its many, *many* dependencies
on other coin\_or projects.
### danbooru\_atomizer
a Python script that scrapes atom feeds on [danbooru (nsfw)](https://danbooru.donmai.us/)
@ -62,14 +57,6 @@ for a given search query. requires zsh and xml2.
i don't think this version works anymore,
so i'll have to update it with my local changes sometime.
### debug\_saves
for Majora's Mask:
these gameshark cheats will override the default save file
with the debug save file. create a new file on the file select screen
and it should create a debug save.
*note:* this is probably the same as using map select.
### desmos
[stuff i've plotted in desmos.](/desmos/desmos.md)
@ -115,6 +102,10 @@ with all the appropriate character mappings.
the code is Unlicensed, and i believe the font itself is public domain.
### explicit\_globals
hmm, this probably shouldn't be here...
### filter\_tutorial
a single Python file that walks you through
@ -138,7 +129,11 @@ the batch script exploits [a UAC bypass found by Tyranid.](https://tyranidslair.
### kyaa
[*moved to its own repo*](/git/notwa/kyaa)
really *really* hacky argument-parsing macros for C programs.
contains [some ad-hoc documentation.](/kyaa/kyaa.md)
This is free and unencumbered software released into the public domain.
### lsca
@ -188,10 +183,6 @@ and even runs out of memory if too many files need updating (thanks mutagenx).
a hastily-written Python script for outlining the F3DEX model format
used in some N64 games.
### phasmophobia
an AutoHotKey script for quickly adding and removing equipment to games.
### polyphase\_halfband
a C port (from C++) of the polyphase halfband filter coefficient generator
@ -210,14 +201,15 @@ since the resulting format cannot be deserialized,
this is primarily intended for debugging purposes,
and i'd like to think it excels at that.
### response
### psnip\_clock
some old, brief notes on biquads.
### rng64
some notes on the random number generator that Ocarina of Time
and Majora's Mask use. don't get your hopes up about the manipulation stuff.
a fork of the clock utility header file
from [Portable Snippets](https://github.com/nemequ/portable-snippets)
by Evan Nemerson.
this fork removes the dependency on `windows.h` for the Windows backend
by using some gross hacks,
albeit not as gross as actually having to include `windows.h`.
this is licensed under [CC0 1.0.](https://creativecommons.org/publicdomain/zero/1.0/)
### speedrun\_comparison

280
atttt/atttt.py Executable file
View File

@ -0,0 +1,280 @@
#!/usr/bin/env python3
import sys
import numpy as np
from misc import *
from basic import Brain
def align(x, alignment):
return (x + alignment // 2) // alignment * alignment
def uniq_rows(a, return_index=False, return_inverse=False, return_counts=False):
# via http://stackoverflow.com/a/16973510
# black magic wrapper around np.unique
return_any = return_index or return_inverse or return_counts
if not return_any:
np.unique(a.view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
else:
void_dtype = np.dtype((np.void, a.dtype.itemsize * a.shape[1]))
ret = np.unique(a.view(void_dtype), return_index, return_inverse, return_counts)
return (ret[0].view(a.dtype).reshape(-1, a.shape[1]),) + ret[1:]
class ATTTT():
def __init__(self, brain):
self.brain = brain
self.score = self._score
def _score(self, reply, maxn):
if len(reply) > maxn:
return -999999999
#return len(reply)
return 1
def reply(self, item=None, maxn=1000, include_scores=False, attempts=None):
if attempts == None:
# just guess some value that'll take roughly the same amount of time
attempts = int(2**12 / self.brain.order)
lament('attempts:', attempts)
replies = []
for i in range(attempts):
reply = "".join(self.brain.reply(item=item, maxn=maxn+1))
replies += [(reply, self.score(reply, maxn))]
result = sorted(replies, key=lambda t: t[1], reverse=True)[0]
if include_scores:
return result
else:
return result[0]
class PatternBrain(Brain):
def __init__(self, *args, **kwargs):
super().__init__(*args, padding='~', **kwargs)
self.tokens = []
def helper(self, v):
return (v,)
def resolve_tokens(self, tokens):
# positive values are just unicode characters
if isinstance(tokens, int) or isinstance(tokens, np.int32):
return tokens < 0 and self.tokens[tokens] or chr(tokens)
else:
return [o < 0 and self.tokens[o] or chr(o) for o in tokens]
def new_token(self, value):
new_id = -1 - len(self.tokens)
self.tokens[new_id] = value
return new_id
@staticmethod
def prepare_items(items, pad=True):
new_items = []
for item in items:
item = item.strip('\n')
# assert that the number of sequences is a multiple of 2
# otherwise we can't .reshape() it to be two-dimensional later on
next_biggest = align(len(item) + 1, 2)
# initialize with padding (-1)
new_item = -np.ones(next_biggest, dtype=np.int32)
for i, c in enumerate(item):
new_item[i] = ord(c)
new_items.append(new_item)
# add an extra padding item to the head and tail
# to make it easier to convert from sequences back to items later on
if pad:
pad = -np.ones(1, dtype=np.int32)
new_items.insert(0, pad)
new_items.append(pad)
return np.concatenate(new_items)
def stat_tokens(self, all_items, skip_normal=False):
unique, counts = np.unique(all_items, return_counts=True)
count_order = np.argsort(counts)[::-1]
counts_descending = counts[count_order]
unique_descending = unique[count_order]
for i, token_id in enumerate(unique_descending):
if token_id == -1:
continue
if skip_normal and token_id >= 0:
continue
token = self.resolve_tokens(token_id)
lament("token id {:5} occurs {:8} times: \"{}\"".format(
token_id, counts_descending[i], token))
lament("total tokens: {:5}".format(i + 1))
def merge_all(self, all_items, merges, min_count=2):
# set up a 2d array to step through at half the row length;
# this means double redundancy; to acquire all the sequences.
# we could instead .roll it later to get the other half.
# that would require less memory, but memory isn't really a concern.
sequences = all_items.repeat(2)[1:-1].reshape(-1, 2).copy()
for i in range(merges):
invalid = np.any(sequences == -1, axis=1)
valid_sequences = np.delete(sequences, np.where(invalid), axis=0)
unique, counts = uniq_rows(valid_sequences, return_counts=True)
count = counts.max()
most_common = (None, 1)
if count > most_common[1]:
seq = unique[counts == count][0]
most_common = (seq, count)
if most_common[0] is None or most_common[1] <= 1 or most_common[1] < min_count:
lament('no more valid sequences')
break
token_value = "".join(self.resolve_tokens(most_common[0]))
new_id = self.new_token(token_value)
# replace the most common two-token sequence
# with one token to represent both
found = np.all(sequences == most_common[0], axis=1)
before = np.roll(found, -1)
after = np.roll(found, 1)
# don't wrap around truth values
before[-1] = False
after[0] = False
# remove the "found" sequences
# and update the previous/next,
# not unlike a doubly-linked list.
befores = sequences[before].T.copy()
befores[1] = new_id
sequences[before] = befores.T
afters = sequences[after].T.copy()
afters[0] = new_id
sequences[after] = afters.T
here = np.where(found)
sequences = np.delete(sequences, here, axis=0)
lament("new token id {:5} occurs {:8} times: \"{}\"".format(
new_id, len(here[0]), self.tokens[new_id]))
# reconstruct all_items out of the sequences
all_items = sequences.reshape(-1)[::2][1:].copy()
return all_items
def learn_all(self, items, merges=0, stat=True):
min_count = 2 # minimum number of occurences to stop creating tokens at
if merges < 0:
min_count = -merges
merges = 65536 # arbitrary sanity value
# we'll use numpy matrices so this isn't nearly as disgustingly slow
self.tokens = {-1: ''} # default with an empty padding token
all_items = self.prepare_items(items)
if merges > 0:
all_items = self.merge_all(all_items, merges, min_count)
# begin the actual learning
self.reset()
np_item = []
for i in all_items:
if i == -1:
if len(np_item) == 0:
continue
item = tuple()
for i in np_item:
if i < 0:
assert(i != -1)
item += self.helper(self.tokens[i])
else:
item += self.helper(chr(i))
#die(np_item, item)
self.learn(item)
np_item = []
else:
np_item.append(i)
self.update()
if merges != 0 and stat:
self.stat_tokens(all_items)
def run(pname, args, env):
if not 1 <= len(args) <= 2:
lament("usage: {} {{input file}} [savestate file]".format(pname))
return 1
args = dict(enumerate(args)) # just for the .get() method
fn = args[0]
state_fn = args.get(1, None)
# the number of lines to output.
count = int(env.get('COUNT', '8'))
# learn and sample using this number of sequential tokens.
order = int(env.get('ORDER', '2'))
# how experimental to be with sampling.
# probably doesn't work properly.
temperature = float(env.get('TEMPERATURE', '0.5'))
# the max character length of output. (not guaranteed)
maxn = int(env.get('MAXN', '240'))
# attempts to maximize scoring
attempts = int(env.get('ATTEMPTS', '-1'))
# if positive, maximum number of tokens to merge.
# if negative, minimum number of occurences to stop at.
merges = int(env.get('MERGES', '0'))
if attempts <= 0:
attempts = None
brain = PatternBrain(order=order, temperature=temperature)
tool = ATTTT(brain)
if state_fn:
lament('# loading')
try:
brain.load(state_fn, raw=False)
except FileNotFoundError:
lament('# no file to load. skipping')
pass
if brain and brain.new:
lament('# learning')
lines = open(fn).readlines()
brain.learn_all(lines, merges)
if brain and brain.new and state_fn:
lament('# saving')
brain.save(state_fn, raw=False)
lament('# replying')
for i in range(count):
#reply = tool.reply(maxn=maxn, raw=True, attempts=attempts)
#print('{:6.1f}\t{}'.format(reply[1], reply[0]))
print(tool.reply(maxn=maxn, attempts=attempts))
return 0
if __name__ == '__main__':
import sys
import os
pname = len(sys.argv) > 0 and sys.argv[0] or ''
args = len(sys.argv) > 1 and sys.argv[1:] or []
sys.exit(run(pname, args, os.environ))

189
atttt/basic.py Executable file
View File

@ -0,0 +1,189 @@
import math
import numpy as np
from misc import *
def normalize(counter):
v = counter.values()
s = float(sum(v))
m = float(max(v))
del v
return [(c, cnt/s, cnt/m) for c, cnt in counter.items()]
def normalize_sorted(counter):
# if the elements were unsorted,
# we couldn't use our lazy method (subtraction) of selecting tokens
# and temperature would correspond to arbitrary tokens
# instead of more/less common tokens.
return sorted(normalize(counter), key=lambda t: t[1], reverse=True)
# http://nbviewer.jupyter.org/gist/yoavg/d76121dfde2618422139
class Brain:
def __init__(self, padding, order=1, temperature=0.5):
self.order = order
self.temperature = temperature
self.padding = padding
self.reset()
def reset(self):
import collections as cool
# unnormalized
self._machine = cool.defaultdict(cool.Counter)
# normalized
self.machine = None
self.type = None
self.dirty = False
self.new = True
@property
def temperature(self):
return self._temperature
@temperature.setter
def temperature(self, value):
assert(0 < value < 1)
self._temperature = value
a = 1 - value * 2
# http://www.mathopenref.com/graphfunctions.html?fx=(a*x-x)/(2*a*x-a-1)&sg=f&sh=f&xh=1&xl=0&yh=1&yl=0&ah=1&al=-1&a=0.5
tweak = lambda x: (a * x - x) / (2 * a * x - a - 1)
self.random = lambda n: 1 - tweak(np.random.random(n))
def learn_all(self, items):
for item in items:
self.learn(item)
self.update()
def learn(self, item):
assert(self.padding)
if self.type is None and item is not None:
self.type = type(item)
if type(item) is not self.type:
raise Exception("that's no good")
if self.type == type("string"):
item = item.strip()
if len(item) == 0:
return
pad = self.helper(self.padding) * self.order
item = pad + item + pad
stop = len(item) - self.order
if stop > 0:
for i in range(stop):
history, newitem = item[i:i+self.order], item[i+self.order]
self._machine[history][newitem] += 1
self.dirty = True
def update(self):
if self.dirty and self._machine:
self.machine = {hist: normalize_sorted(items)
for hist, items in self._machine.items()}
self.dirty = False
def next(self, history):
history = history[-self.order:]
dist = self.machine.get(history, None)
if dist == None:
lament('warning: no value: {}'.format(history))
return None
x = self.random(1)
for c, cs, cm in dist:
x = x - cs
if x <= 0:
return c
# for overriding in subclasses
# in case the input tokens aren't strings (e.g. tuples)
def helper(self, v):
return v
def reply(self, item=None, maxn=1000):
assert(self.padding)
self.update()
history = self.helper(self.padding) * self.order
out = []
for i in range(maxn):
c = self.next(history)
if c.find(self.padding) != -1:
out.append(c.replace(self.padding, ''))
break
history = history[-self.order:] + self.helper(c)
out.append(c)
return out
def load(self, fn, raw=True):
import pickle
if type(fn) == type(''):
f = open(fn, 'rb')
else:
f = fn
d = pickle.load(f)
if d['order'] != self.order:
lament('warning: order mismatch. cancelling load.')
return
self.order = d['order']
if raw:
if not d.get('_machine'):
lament('warning: no _machine. cancelling load.')
return
self._machine = d['_machine']
self.dirty = True
self.update()
else:
if not d.get('machine'):
lament('warning: no machine. cancelling load.')
return
self.machine = d['machine']
self.new = False
if f != fn:
f.close()
def save(self, fn, raw=True):
import pickle
if type(fn) == type(''):
f = open(fn, 'wb')
else:
f = fn
d = {}
d['order'] = self.order
if raw:
d['_machine'] = self._machine
else:
d['machine'] = self.machine
pickle.dump(d, f)
if f != fn:
f.close()

11
atttt/misc.py Executable file
View File

@ -0,0 +1,11 @@
import sys
lament = lambda *args, **kwargs: print(*args, file=sys.stderr, **kwargs)
def die(*args, **kwargs):
# just for ad-hoc debugging really
lament(*args, **kwargs)
sys.exit(1)
__all__ = [o for o in locals() if type(o) != 'module' and not o.startswith('_')]

View File

@ -0,0 +1,25 @@
local mt = getmetatable(_G)
if mt == nil then
mt = {}
setmetatable(_G, mt)
end
mt.__declared = {}
function mt.__newindex(t, n, v)
if not mt.__declared[n] then
local info = debug.getinfo(2, "S")
if info and info.what ~= "main" and info.what ~= "C" then
error("cannot assign undeclared global '" .. tostring(n) .. "'", 2)
end
mt.__declared[n] = true
end
rawset(t, n, v)
end
function mt.__index(t, n)
if not mt.__declared[n] then
local info = debug.getinfo(2, "S")
if info and info.what ~= "main" and info.what ~= "C" then
error("cannot use undeclared global '" .. tostring(n) .. "'", 2)
end
end
end

32
gs/gs.lua Normal file
View File

@ -0,0 +1,32 @@
package.path = package.path..";./?/init.lua"
local lips = require "lips"
function make_gameshark_writer()
local buff = {}
local max = -1
return function(pos, b)
if pos then
pos = pos % 0x80000000
buff[pos] = b
if pos > max then
max = pos
end
elseif max >= 0 then
for i=0, max, 2 do
local a = buff[i+0]
local b = buff[i+1]
if a and b then
print(('81%06X %s'):format(i, a..b))
elseif a then
print(('80%06X 00%s'):format(i, a))
elseif b then
print(('80%06X 00%s'):format(i + 1, b))
end
end
end
end
end
local gs = make_gameshark_writer()
lips('test.asm', gs, {unsafe=true, offset=0x80000000})
gs()

76
gs/test.asm Normal file
View File

@ -0,0 +1,76 @@
;Free Inventory Movement
;This will make it so that you can place the cursor on empty
;Cursor Movement:
;LEFT
.org 0x803D8B88
NOP
;RIGHT
.org 0x803D8C68
NOP
;FROM L
.org 0x803D8DD0
.skip 8 0 ;NOP the next 2 commands
;FROM R
.org 0x803D8EA8
.skip 8 0 ;NOP the next 2 commands
;UP
.org 0x803D8FAC
NOP
;DOWN
.org 0x803D9004
NOP
;Hide Item 0xFF name (on select)
.org 0x803E2168
lhu a1, 0x023C(v0) ;
lhu v1, 0x002A(sp) ;Already there
slti at, a1, 0x00FF ;
beql at, zero, 0x803E2338
;Hide Item 0xFF name (loop)
.org 0x803E1370
slti at, t7, 0x00FF ;Set at to 1 if you don't have item 0xFF or a blank slot selected
.org 0x803E1378
beql at, zero, 0x803E17E8 ;Branch if you have item 0xFF or a blank slot selected
;Set Cursor to Default on 0xFF
.org 0x803D9058
slti t5, a0, 0x00FF ;t5 = 1 if you have selected an item
sll t5, t5, 0x0002 ;t5 = t5<<0x2
NOP ;
;Make Item 0xFF unequippable (2.0)
.org 0x803D9140
addiu at, zero, 0x00FF ;at = 0xFF (Blank)
lw t6, 0x0048(sp) ;t6 = Item ID
beq at, t6, 0x803D92FC ;Branch past error sound if item is 0xFF (Blank)
addiu at, zero, 0x0009 ;at = 0x0009 (Usable by both)
beq v0, at, + ;Branch if usable by child and adult
lw t5, 0x0004(s4) ;t5 = Age
bne v0, t5, 0x803D92D0 ;Branch to error sound if wrong age
+:
NOP ;Remove command
;Hide text when on empty slot
.org 0x803E1B80
lhu v0, 0x023E(t0) ;v0 = Selected Item ID
slti v0, v0, 0x00FF ;v0 = 1 if Selected Item ID is less than 0xFF
beq v0, zero, 0x803E1D0C ;Branch if item ID is 0xFF or higher
;Optimized code below
lbu t6, 0x1409(t2)
lui t7, 0x8016
lw t9, 0xFA90(t7)
sll t8, t6, 0x1
addu v0, t9, t8
lw t9, 0x0154(t0)
lh v0, 0x0DF6(v0)
sh v0, 0x0120(t9)
lui a1, 0x803F ;This command need a offset relocation fix. 0x1470 must be added. 0x0000F7FC
addiu t3, a1, 0x9968

113
kyaa/kyaa.h Normal file
View File

@ -0,0 +1,113 @@
/* kyaa.h - macro hacks for handling main() arguments
This is free and unencumbered software released into the public domain.
Refer to kyaa.md for documentation.
*/
#pragma once
#ifndef KYAA_OKAY
#define KYAA_OKAY 0
#endif
#ifndef KYAA_ERROR
#define KYAA_ERROR 1
#endif
#ifndef KYAA_ITER
#define KYAA_ITER i
#endif
#define KYAA_SETUP \
/* sanity checks */ \
if (argc <= 0 || argv == NULL || argv[0] == NULL) { \
fprintf(stderr, "You've met with a terrible fate.\n"); \
return KYAA_ERROR; \
} \
char *kyaa_name = argv[0]; \
bool kyaa_read_stdin = false; \
char kyaa_flag = '\0'; \
bool kyaa_keep_parsing = true; \
bool kyaa_parse_next = false; \
#define KYAA_LOOP \
KYAA_SETUP \
for (int KYAA_ITER = 1; KYAA_ITER < argc; KYAA_ITER++) \
#define KYAA_BEGIN \
char *kyaa_arg = argv[KYAA_ITER]; \
if (kyaa_keep_parsing && (kyaa_parse_next || kyaa_arg[0] == '-')) { \
if (!kyaa_parse_next && kyaa_arg[1] == '-' && kyaa_arg[2] == '\0') { \
kyaa_keep_parsing = false; \
continue; \
} \
if (!kyaa_parse_next && kyaa_arg[1] == '\0') { \
kyaa_read_stdin = true; \
} else { \
/* case: kyaa_parse_next: arg is at least 1 char initialized. */ \
/* case: !kyaa_parse_next: arg is at least 3 chars initialized. */ \
char *kyaa_etc = kyaa_parse_next ? kyaa_arg : kyaa_arg + 2; \
bool kyaa_no_more = false; \
bool kyaa_helping = false; \
bool kyaa_any = false; \
if (!kyaa_parse_next && kyaa_arg[1] != '-') { \
kyaa_flag = kyaa_arg[1]; \
kyaa_no_more = kyaa_arg[2] == '\0'; \
} \
if (kyaa_flag == 'h' || !strcmp(kyaa_arg, "--help")) { \
printf("usage:\n"); \
kyaa_helping = true; \
} \
if (0) { \
#define KYAA_END \
} \
if (!kyaa_any && !kyaa_helping) { \
if (kyaa_flag) { \
fprintf(stderr, "unknown flag: -%c\n", kyaa_flag); \
} else { \
fprintf(stderr, "unknown flag: %s\n", kyaa_arg); \
} \
return KYAA_ERROR; \
} \
if (kyaa_helping) { \
return KYAA_OKAY; \
} \
kyaa_parse_next = false; \
kyaa_flag = '\0'; \
continue; \
} \
} \
#define KYAA_DESCRIBE(c, name, description) \
printf(" -%c --%s\n%s\n", c, name, description) \
#define KYAA_FLAG(c, name, description) \
} \
if (kyaa_helping) { \
KYAA_DESCRIBE(c, name, description); \
} else if (kyaa_flag == c || !strcmp(kyaa_arg, "--"name)) { \
kyaa_flag = c; \
kyaa_any = true; \
#define KYAA_FLAG_ARG(c, name, description) \
} \
if (kyaa_helping) { \
KYAA_DESCRIBE(c, name, description); \
} else if (kyaa_flag == c || !strcmp(kyaa_arg, "--"name)) { \
if (kyaa_no_more || kyaa_flag != c) { \
kyaa_parse_next = true; \
if (KYAA_ITER + 1 == argc || argv[KYAA_ITER + 1][0] == '\0') { \
fprintf(stderr, "expected an argument for --%s (-%c)\n", name, c); \
return KYAA_ERROR; \
} \
kyaa_flag = c; \
continue; \
} \
kyaa_flag = c; \
kyaa_any = true; \
#define KYAA_HELP(description) \
} \
if (kyaa_helping) { \
if (description != NULL) { \
printf("%s\n", description); \
} \

160
kyaa/kyaa.md Normal file
View File

@ -0,0 +1,160 @@
# kyaa
super hacky macro hacks for parsing arguments in C.
## prerequisites
C99 or greater.
standard library headers:
* `stdbool.h`
* `stdio.h`
* `string.h`
in addition, `kyaa_extra.h` requires `limits.h`,
or at least `LONG_MIN` to be defined.
## tutorial
ensure `argc` and `argv` are defined.
kyaa doesn't actually care if it's in `main` or not.
iterate over the arguments with `KYAA_LOOP`:
```c
int main(int argc, char *argv[]) {
KYAA_LOOP {
// KYAA_ITER, kyaa_name, kyaa_read_stdin, and kyaa_flag are exposed here.
// [other code goes here]
}
return 0;
}
```
use `KYAA_BEGIN` and `KYAA_END` to begin parsing arguments.
put unrelated code above `KYAA_BEGIN` or below `KYAA_END`, but not within:
```c
KYAA_LOOP {
// [other code goes here]
KYAA_BEGIN
// kyaa_arg and kyaa_etc are exposed here.
// [kyaa code goes here]
KYAA_END
// [other code goes here]
}
```
use `KYAA_FLAG` for defining flags that don't take an argument,
and `KYAA_FLAG_ARG` or `KYAA_FLAG_LONG` for flags that do:
```c
bool use_feature = false;
char *log_fn = "logs.txt";
long my_var = 0;
KYAA_LOOP {
// [other code goes here]
KYAA_BEGIN
// arguments: short flag, long flag, help description
KYAA_FLAG("-x", "--enable-feature",
" enable some feature")
use_feature = true;
// same arguments, but kyaa_etc contains the relevant string.
KYAA_FLAG_ARG("-l", "--log-file",
" use a given filename for the log file")
log_fn = kyaa_etc;
// same arguments, kyaa_etc is set, but kyaa_flag_arg is also set.
KYAA_FLAG_LONG("-v", "--var",
" set an integer variable\n"
" default: 0")
my_var = kyaa_flag_arg;
KYAA_END
// [other code goes here]
}
```
kyaa secretly wraps flag handling in if/else statements with {} blocks.
do not confuse it for a switch/case method.
kyaa handles `-h` and `--help` for printing help text.
additional help text may be defined using `KYAA_HELP`:
```c
KYAA_LOOP {
KYAA_BEGIN
KYAA_HELP(
" {files...}\n"
" do things with files.")
KYAA_END
do_stuff(kyaa_arg);
}
```
kyaa interprets an argument of `-` as a request to enable reading from stdin:
`kyaa_read_stdin` is set to true.
arguments may be passed in three ways, consider:
* `-v42`
* `-v 42`
* `--var 42`
kyaa returns `KYAA_OKAY` when `-h` or `--help` is given,
and `KYAA_ERROR` in the event of invalid flags, missing arguments,
or invalid numbers (`KYAA_FLAG_LONG`).
kyaa uses `continue` for handling arguments.
kyaa prints error messages to `stderr`, and help text to `stdout`.
`KYAA_ITER` may be redefined to avoid name collisions.
## TODO
* support `--var=42` argument style
* fix overlapping names, e.g. `KYAA_FLAG` vs `kyaa_flag`, `KYAA_FLAG_ARG` vs `kyaa_flag_arg`, etc.
* maybe pass `argc`/`argv` manually?
## API
### kyaa.h
return type or type | name | arguments or default value
--- | --- | ---
define | KYAA\_OKAY | 0
define | KYAA\_ERROR | 1
define | KYAA\_ITER | i
||
macro | KYAA\_SETUP | *none*
macro | KYAA\_LOOP | *none*
macro | KYAA\_BEGIN | *none*
macro | KYAA\_END | *none*
macro | KYAA\_DESCRIBE | c, name, description
macro | KYAA\_FLAG | c, name, description
macro | KYAA\_FLAG\_ARG | c, name, description
macro | KYAA\_HELP | description
||
`char *` | kyaa\_name | *n/a*
`bool` | kyaa\_read\_stdin | *n/a*
`char *` | kyaa\_arg | *n/a*
|| **internal use** ||
`char` | kyaa\_flag | *n/a*
`bool` | kyaa\_keep\_parsing | *n/a*
`bool` | kyaa\_parse\_next | *n/a*
`bool` | kyaa\_no\_more | *n/a*
`bool` | kyaa\_helping | *n/a*
`bool` | kyaa\_any | *n/a*
### kyaa\_extra.h
return value or type | name | arguments or default value
--- | --- | ---
macro | KYAA\_FLAG\_LONG | c, name, description
||
`char *` | kyaa\_flag\_arg | *n/a*
||
`char *` | kyaa\_skip\_spaces | `char *` str
`const char *` | kyaa\_str\_to\_long | `char *` str, `long *` p\_out

211
kyaa/kyaa_extra.h Normal file
View File

@ -0,0 +1,211 @@
/* kyaa_extra.h - extensions to kyaa for parsing arguments.
This is free and unencumbered software released into the public domain.
Refer to kyaa.md for documentation.
*/
static char *kyaa_skip_spaces(char *str) {
/* iterates str to first non-space character according to the C locale */
while (*str != '\0') {
switch (*str) {
case ' ':
case '\f':
case '\n':
case '\r':
case '\t':
case '\v': { str++; } break;
default: return str;
}
}
return str;
}
static const char *kyaa__base_2(char **p_str, long *p_out) {
char *str = *p_str;
long out = *p_out;
for (char c; (c = *str) != '\0'; str++) {
switch (c) {
case '0': case '1': {
long digit = (long)(c - '0');
if (out < (LONG_MIN + digit) / 2) {
return "out of range for long integer";
}
out = out * 2 - digit;
} break;
case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case '.': return "invalid character for base 2 integer";
default: goto exit;
}
}
exit:
*p_str = str;
*p_out = out;
return NULL;
}
static const char *kyaa__base_8(char **p_str, long *p_out) {
char *str = *p_str;
long out = *p_out;
for (char c; (c = *str) != '\0'; str++) {
switch (c) {
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7': {
long digit = (long)(c - '0');
if (out < (LONG_MIN + digit) / 8) {
return "out of range for long integer";
}
out = out * 8 - digit;
} break;
case '8': case '9':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case '.': return "invalid character for base 8 integer";
default: goto exit;
}
}
exit:
*p_str = str;
*p_out = out;
return NULL;
}
static const char *kyaa__base_10(char **p_str, long *p_out) {
char *str = *p_str;
long out = *p_out;
for (char c; (c = *str) != '\0'; str++) {
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': {
long digit = (long)(c - '0');
if (out < (LONG_MIN + digit) / 10) {
return "out of range for long integer";
}
out = out * 10 - digit;
} break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case '.': return "invalid character for base 10 integer";
default: goto exit;
}
}
exit:
*p_str = str;
*p_out = out;
return NULL;
}
static const char *kyaa__base_16(char **p_str, long *p_out) {
char *str = *p_str;
long out = *p_out;
for (char c; (c = *str) != '\0'; str++) {
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': {
long digit = (long)(c - '0');
if (out < (LONG_MIN + digit) / 16) {
return "out of range for long integer";
}
out = out * 16 - digit;
} break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': {
long digit = (long)(c - 'A') + 10;
if (out < (LONG_MIN + digit) / 16) {
return "out of range for long integer";
}
out = out * 16 - digit;
} break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': {
long digit = (long)(c - 'a') + 10;
if (out < (LONG_MIN + digit) / 16) {
return "out of range for long integer";
}
out = out * 16 - digit;
} break;
case '.': return "invalid character for base 16 integer";
default: goto exit;
}
}
exit:
*p_str = str;
*p_out = out;
return NULL;
}
static const char *kyaa_str_to_long(char *str, long *p_out) {
/* returns error message or NULL */
long out = 0;
int base = 10;
bool negated = false;
str = kyaa_skip_spaces(str);
switch (*str) {
case '-': { negated = true; str++; } break;
case '+': { str++; } break;
default: break;
}
switch (*str) {
case '#': { base = 10; str++; } break;
case '$': { base = 8; str++; } break;
case '%': { base = 2; str++; } break;
case '0': { base = -1; str++; } break;
default: break;
}
if (base == -1) {
switch (*str) {
case '\0': { *p_out = 0; } return NULL;
case 'b': { base = 2; str++; } break;
case 'h': { base = 16; str++; } break;
case 'o': { base = 8; str++; } break;
case 'x': { base = 16; str++; } break;
default: { base = 8; } break;
}
}
if (*str == '\0') return "no number given";
// NOTE: we actually subtract each digit from the result instead of summing.
// this lets us represent LONG_MIN without overflowing.
const char *err;
switch (base) {
case 2: { err = kyaa__base_2( &str, &out); } break;
case 8: { err = kyaa__base_8( &str, &out); } break;
case 10: { err = kyaa__base_10(&str, &out); } break;
case 16: { err = kyaa__base_16(&str, &out); } break;
default: return "internal error";
}
if (err != NULL) return err;
str = kyaa_skip_spaces(str);
if (*str != '\0') return "unexpected character";
// NOTE: out is negative here; see above comment.
// assuming two's complement
if (!negated && out == LONG_MIN) return "out of range for long integer";
*p_out = negated ? out : -out;
return NULL;
}
#define KYAA_FLAG_LONG(c, name, description) \
KYAA_FLAG_ARG(c, name, description) \
long kyaa_flag_arg; \
const char *err = kyaa_str_to_long(kyaa_etc, &kyaa_flag_arg); \
if (err) { \
fprintf(stderr, "%s\n", err); \
return KYAA_ERROR; \
} \

View File

@ -1,125 +0,0 @@
#SingleInstance Force
#IfWinActive ahk_exe Phasmophobia.exe
Return
; 1 = EMF Reader
; 2 = Flashlight
; 3 = Photo Camera
; 4 = Lighter
; 5 = Candle
; 6 = UV Light
; 7 = Crucifix
; 8 = Video Camera
; 9 = Spirit Box
; 10 = Salt
; 11 = Smudge Sticks
; 12 = Tripod
; 13 = Strong Flashlight
; 14 = Motion Sensor
; 15 = Sound Sensor
; 16 = Thermometer
; 17 = Sanity Pills
; 18 = Ghost Writing Book
; 19 = Infrared Light Sensor
; 20 = Parabolic Microphone
; 21 = Glowstick
; 22 = Head Mounted Camera
*,::
; add just the stuff you need.
Prologue("ahk_class UnityWndClass", mouseX, mouseY)
PhasmoAdd(2) ; Flashlight
PhasmoAdd(3) ; Photo Camera
PhasmoAdd(4) ; Lighter
PhasmoAdd(7) ; Crucifix
PhasmoAdd(8) ; Video Camera
PhasmoAdd(10) ; Salt
PhasmoAdd(11) ; Smudge Sticks
PhasmoAdd(12,2) ; Tripod (x2)
PhasmoAdd(13,2) ; Strong Flashlight (x2)
PhasmoAdd(14) ; Motion Sensor
PhasmoAdd(16) ; Thermometer
PhasmoAdd(17,4) ; Sanity Pills (x4)
PhasmoAdd(21) ; Glowstick
PhasmoAdd(22) ; Head Mounted Camera
Epilogue(mouseX, mouseY)
Return
*;::
; add one of everything.
Prologue("ahk_class UnityWndClass", mouseX, mouseY)
Loop 22
PhasmoAdd(A_Index)
Epilogue(mouseX, mouseY)
Return
*'::
; remove one of everything.
Prologue("ahk_class UnityWndClass", mouseX, mouseY)
Loop 22
PhasmoRemove(A_Index)
Epilogue(mouseX, mouseY)
Return
; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
; internal stuff from hereon! ;
; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
GetClientSize(hwnd, ByRef w, ByRef h)
{
VarSetCapacity(rc, 16)
DllCall("GetClientRect", "uint", hwnd, "uint", &rc)
w := NumGet(rc, 8, "int")
h := NumGet(rc, 12, "int")
}
Prologue(hint, ByRef mouseX, ByRef mouseY)
{
global winW, winH
hwnd := WinExist(hint)
GetClientSize(hwnd, winW, winH)
SetDefaultMouseSpeed 0
SetMouseDelay 0
CoordMode Mouse, Screen
MouseGetPos mouseX, mouseY ; store mouse position to restore later
CoordMode Mouse, Client
}
Epilogue(mouseX, mouseY)
{
CoordMode Mouse, Screen
MouseMove mouseX, mouseY ; restore original mouse position
Sleep 100 ; prevent accidental repetition of macro
}
PhasmoClick(relX, relY, n:=1)
{
global winW, winH
fakeW := 1.6 * winH
off := (winW - fakeW) / 2
MouseMove % off + fakeW * relX, winH * relY
Sleep 10
Click
Loop % n - 1
{
Sleep 60
Click
}
Sleep 10
}
PhasmoAdd(id, n:=1)
{
If id between 1 and 15
PhasmoClick(0.41, (id + 11.5) / 37.1, n)
If id between 16 and 22
PhasmoClick(0.72, (id - 3.5) / 37.1, n)
}
PhasmoRemove(id, n:=1)
{
If id between 1 and 15
PhasmoClick(0.435, (id + 11.5) / 37.1, n)
If id between 16 and 22
PhasmoClick(0.745, (id - 3.5) / 37.1, n)
}

478
psnip_clock/clock.h Normal file
View File

@ -0,0 +1,478 @@
/* Clocks (v1)
* Portable Snippets - https://github.com/nemequ/portable-snippets
* Created by Evan Nemerson <evan@nemerson.com>
*
* To the extent possible under law, the authors have waived all
* copyright and related or neighboring rights to this code. For
* details, see the Creative Commons Zero 1.0 Universal license at
* https://creativecommons.org/publicdomain/zero/1.0/
*
* Modified by Connor Olding, 2017
*/
#if !defined(PSNIP_CLOCK_H)
#define PSNIP_CLOCK_H
#if !defined(PSNIP_CLOCK_STATIC_INLINE)
# if defined(__GNUC__)
# define PSNIP_CLOCK__COMPILER_ATTRIBUTES __attribute__((__unused__))
# else
# define PSNIP_CLOCK__COMPILER_ATTRIBUTES
# endif
# define PSNIP_CLOCK__FUNCTION PSNIP_CLOCK__COMPILER_ATTRIBUTES static
#endif
enum PsnipClockType {
/* This clock provides the current time, in units since 1970-01-01
* 00:00:00 UTC not including leap seconds. In other words, UNIX
* time. Keep in mind that this clock doesn't account for leap
* seconds, and can go backwards (think NTP adjustments). */
PSNIP_CLOCK_TYPE_WALL = 1,
/* The CPU time is a clock which increases only when the current
* process is active (i.e., it doesn't increment while blocking on
* I/O). */
PSNIP_CLOCK_TYPE_CPU = 2,
/* Monotonic time is always running (unlike CPU time), but it only
ever moves forward unless you reboot the system. Things like NTP
adjustments have no effect on this clock. */
PSNIP_CLOCK_TYPE_MONOTONIC = 3
};
struct PsnipClockTimespec {
uint64_t seconds;
uint64_t nanoseconds;
};
/* Methods we support: */
#define PSNIP_CLOCK_METHOD_CLOCK_GETTIME 1
#define PSNIP_CLOCK_METHOD_TIME 2
#define PSNIP_CLOCK_METHOD_GETTIMEOFDAY 3
#define PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER 4
#define PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME 5
#define PSNIP_CLOCK_METHOD_CLOCK 6
#define PSNIP_CLOCK_METHOD_GETPROCESSTIMES 7
#define PSNIP_CLOCK_METHOD_GETRUSAGE 8
#define PSNIP_CLOCK_METHOD_GETSYSTEMTIMEPRECISEASFILETIME 9
#define PSNIP_CLOCK_METHOD_GETTICKCOUNT64 10
#include <assert.h>
#if defined(HEDLEY_UNREACHABLE)
# define PSNIP_CLOCK_UNREACHABLE() HEDLEY_UNREACHABLE()
#else
# define PSNIP_CLOCK_UNREACHABLE() assert(0)
#endif
/* Choose an implementation */
/* #undef PSNIP_CLOCK_WALL_METHOD */
/* #undef PSNIP_CLOCK_CPU_METHOD */
/* #undef PSNIP_CLOCK_MONOTONIC_METHOD */
/* We want to be able to detect the libc implementation, so we include
<limits.h> (<features.h> isn't available everywhere). */
#if defined(__unix__) || defined(__unix) || defined(__linux__)
# include <limits.h>
# include <unistd.h>
#endif
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
/* These are known to work without librt. If you know of others
* please let us know so we can add them. */
# if \
(defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17))) || \
(defined(__FreeBSD__))
# define PSNIP_CLOCK_HAVE_CLOCK_GETTIME
# elif !defined(PSNIP_CLOCK_NO_LIBRT)
# define PSNIP_CLOCK_HAVE_CLOCK_GETTIME
# endif
#endif
#if defined(_WIN32)
# if !defined(PSNIP_CLOCK_CPU_METHOD)
# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_GETPROCESSTIMES
# endif
# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
# endif
#endif
#if defined(__MACH__)
# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
# endif
#endif
#if defined(PSNIP_CLOCK_HAVE_CLOCK_GETTIME)
# include <time.h>
# if !defined(PSNIP_CLOCK_WALL_METHOD)
# if defined(CLOCK_REALTIME_PRECISE)
# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME_PRECISE
# elif !defined(__sun)
# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME
# endif
# endif
# if !defined(PSNIP_CLOCK_CPU_METHOD)
# if defined(_POSIX_CPUTIME) || defined(CLOCK_PROCESS_CPUTIME_ID)
# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_PROCESS_CPUTIME_ID
# elif defined(CLOCK_VIRTUAL)
# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_VIRTUAL
# endif
# endif
# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
# if defined(CLOCK_MONOTONIC_RAW) && !defined(__EMSCRIPTEN__)
# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC_RAW
# elif defined(CLOCK_MONOTONIC_PRECISE)
# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC_PRECISE
# elif defined(_POSIX_MONOTONIC_CLOCK) || defined(CLOCK_MONOTONIC)
# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC
# endif
# endif
#endif
#if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200112L)
# if !defined(PSNIP_CLOCK_WALL_METHOD)
# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_GETTIMEOFDAY
# endif
#endif
#if !defined(PSNIP_CLOCK_WALL_METHOD)
# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_TIME
#endif
#if !defined(PSNIP_CLOCK_CPU_METHOD)
# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK
#endif
/* Primarily here for testing. */
#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) && defined(PSNIP_CLOCK_REQUIRE_MONOTONIC)
# error No monotonic clock found.
#endif
/* Implementations */
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_TIME))
# include <time.h>
#endif
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY))
# include <sys/time.h>
#endif
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64))
typedef struct _FILETIME {
uint32_t dwLowDateTime;
uint32_t dwHighDateTime;
} FILETIME;
typedef union _LARGE_INTEGER {
struct {
uint32_t LowPart;
int32_t HighPart;
};
struct {
uint32_t LowPart;
int32_t HighPart;
} u;
int64_t QuadPart;
} LARGE_INTEGER;
#ifdef __cplusplus
#define PSNIP_EXTERN extern "C"
#else
#define PSNIP_EXTERN
#endif
PSNIP_EXTERN void * __stdcall GetCurrentProcess(void);
PSNIP_EXTERN int __stdcall QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
PSNIP_EXTERN int __stdcall QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
PSNIP_EXTERN int __stdcall GetProcessTimes(
void *hProcess,
FILETIME *lpCreationTime,
FILETIME *lpExitTime,
FILETIME *lpKernelTime,
FILETIME *lpUserTime
);
#endif
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE))
# include <sys/time.h>
# include <sys/resource.h>
#endif
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME))
# include <CoreServices/CoreServices.h>
# include <mach/mach.h>
# include <mach/mach_time.h>
#endif
/*** Implementations ***/
#define PSNIP_CLOCK_NSEC_PER_SEC ((uint32_t) (1000000000ULL))
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME))
PSNIP_CLOCK__FUNCTION uint32_t
psnip_clock__clock_getres (clockid_t clk_id) {
struct timespec res;
int r;
r = clock_getres(clk_id, &res);
if (r != 0)
return 0;
return (uint32_t) (PSNIP_CLOCK_NSEC_PER_SEC / res.tv_nsec);
}
PSNIP_CLOCK__FUNCTION int
psnip_clock__clock_gettime (clockid_t clk_id, struct PsnipClockTimespec* res) {
struct timespec ts;
if (clock_gettime(clk_id, &ts) != 0)
return -10;
res->seconds = (uint64_t) (ts.tv_sec);
res->nanoseconds = (uint64_t) (ts.tv_nsec);
return 0;
}
#endif
PSNIP_CLOCK__FUNCTION uint32_t
psnip_clock_wall_get_precision (void) {
#if !defined(PSNIP_CLOCK_WALL_METHOD)
return 0;
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_WALL);
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY
return 1000000;
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME
return 1;
#else
return 0;
#endif
}
PSNIP_CLOCK__FUNCTION int
psnip_clock_wall_get_time (struct PsnipClockTimespec* res) {
(void) res;
#if !defined(PSNIP_CLOCK_WALL_METHOD)
return -2;
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_WALL, res);
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME
res->seconds = time(NULL);
res->nanoseconds = 0;
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY
struct timeval tv;
if (gettimeofday(&tv, NULL) != 0)
return -6;
res->seconds = tv.tv_sec;
res->nanoseconds = tv.tv_usec * 1000;
#else
return -2;
#endif
return 0;
}
PSNIP_CLOCK__FUNCTION uint32_t
psnip_clock_cpu_get_precision (void) {
#if !defined(PSNIP_CLOCK_CPU_METHOD)
return 0;
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_CPU);
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK
return CLOCKS_PER_SEC;
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES
return PSNIP_CLOCK_NSEC_PER_SEC / 100;
#else
return 0;
#endif
}
PSNIP_CLOCK__FUNCTION int
psnip_clock_cpu_get_time (struct PsnipClockTimespec* res) {
#if !defined(PSNIP_CLOCK_CPU_METHOD)
(void) res;
return -2;
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_CPU, res);
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK
clock_t t = clock();
if (t == ((clock_t) -1))
return -5;
res->seconds = t / CLOCKS_PER_SEC;
res->nanoseconds = (t % CLOCKS_PER_SEC) * (PSNIP_CLOCK_NSEC_PER_SEC / CLOCKS_PER_SEC);
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES
FILETIME CreationTime, ExitTime, KernelTime, UserTime;
LARGE_INTEGER date, adjust;
if (!GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime))
return -7;
/* http://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ */
date.HighPart = UserTime.dwHighDateTime;
date.LowPart = UserTime.dwLowDateTime;
adjust.QuadPart = 11644473600000 * 10000;
date.QuadPart -= adjust.QuadPart;
res->seconds = date.QuadPart / 10000000;
res->nanoseconds = (date.QuadPart % 10000000) * (PSNIP_CLOCK_NSEC_PER_SEC / 100);
#elif PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage) != 0)
return -8;
res->seconds = usage.ru_utime.tv_sec;
res->nanoseconds = tv.tv_usec * 1000;
#else
(void) res;
return -2;
#endif
return 0;
}
PSNIP_CLOCK__FUNCTION uint32_t
psnip_clock_monotonic_get_precision (void) {
#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
return 0;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC);
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
static mach_timebase_info_data_t tbi = { 0, };
if (tbi.denom == 0)
mach_timebase_info(&tbi);
return (uint32_t) (tbi.numer / tbi.denom);
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64
return 1000;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
LARGE_INTEGER Frequency;
QueryPerformanceFrequency(&Frequency);
return (uint32_t) ((Frequency.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC) ? PSNIP_CLOCK_NSEC_PER_SEC : Frequency.QuadPart);
#else
return 0;
#endif
}
PSNIP_CLOCK__FUNCTION int
psnip_clock_monotonic_get_time (struct PsnipClockTimespec* res) {
#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
(void) res;
return -2;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC, res);
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
uint64_t nsec = mach_absolute_time();
static mach_timebase_info_data_t tbi = { 0, };
if (tbi.denom == 0)
mach_timebase_info(&tbi);
nsec *= ((uint64_t) tbi.numer) / ((uint64_t) tbi.denom);
res->seconds = nsec / PSNIP_CLOCK_NSEC_PER_SEC;
res->nanoseconds = nsec % PSNIP_CLOCK_NSEC_PER_SEC;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
LARGE_INTEGER t, f;
if (QueryPerformanceCounter(&t) == 0)
return -12;
QueryPerformanceFrequency(&f);
res->seconds = t.QuadPart / f.QuadPart;
res->nanoseconds = t.QuadPart % f.QuadPart;
if (f.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC)
res->nanoseconds /= f.QuadPart / PSNIP_CLOCK_NSEC_PER_SEC;
else
res->nanoseconds *= PSNIP_CLOCK_NSEC_PER_SEC / f.QuadPart;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64
const ULONGLONG msec = GetTickCount64();
res->seconds = msec / 1000;
res->nanoseconds = sec % 1000;
#else
return -2;
#endif
return 0;
}
/* Returns the number of ticks per second for the specified clock.
* For example, a clock with millisecond precision would return 1000,
* and a clock with 1 second (such as the time() function) would
* return 1.
*
* If the requested clock isn't available, it will return 0.
* Hopefully this will be rare, but if it happens to you please let us
* know so we can work on finding a way to support your system.
*
* Note that different clocks on the same system often have a
* different precisions.
*/
PSNIP_CLOCK__FUNCTION uint32_t
psnip_clock_get_precision (enum PsnipClockType clock_type) {
switch (clock_type) {
case PSNIP_CLOCK_TYPE_MONOTONIC:
return psnip_clock_monotonic_get_precision ();
case PSNIP_CLOCK_TYPE_CPU:
return psnip_clock_cpu_get_precision ();
case PSNIP_CLOCK_TYPE_WALL:
return psnip_clock_wall_get_precision ();
}
PSNIP_CLOCK_UNREACHABLE();
return 0;
}
/* Set the provided timespec to the requested time. Returns 0 on
* success, or a negative value on failure. */
PSNIP_CLOCK__FUNCTION int
psnip_clock_get_time (enum PsnipClockType clock_type, struct PsnipClockTimespec* res) {
assert(res != NULL);
switch (clock_type) {
case PSNIP_CLOCK_TYPE_MONOTONIC:
return psnip_clock_monotonic_get_time (res);
case PSNIP_CLOCK_TYPE_CPU:
return psnip_clock_cpu_get_time (res);
case PSNIP_CLOCK_TYPE_WALL:
return psnip_clock_wall_get_time (res);
}
return -1;
}
#endif /* !defined(PSNIP_CLOCK_H) */

Binary file not shown.

184
resnet/resnet.py Executable file
View File

@ -0,0 +1,184 @@
#!/usr/bin/env python3
import keras.backend as K
assert K.image_dim_ordering() == 'th'
import pickle, time
import sys
import numpy as np
from keras.callbacks import LearningRateScheduler
from keras.datasets import mnist
from keras.layers import BatchNormalization
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Flatten, Reshape
from keras.layers import Input, merge, Dense, Activation
from keras.models import Model
from keras.utils.np_utils import to_categorical
nb_classes = 10
width = 28
height = 28
loss='categorical_crossentropy'
name = 'resnet-{:.0f}'.format(time.time())
args = dict(enumerate(sys.argv))
restore_fn = args.get(1)
if restore_fn == '.': # TODO: accept any directory
# just use most recent resnet-*.pkl file in directory
import os
is_valid = lambda fn: fn.startswith('resnet-') and fn.endswith('.pkl')
files = sorted([fn for fn in os.listdir(restore_fn) if is_valid(fn)])
if len(files) == 0:
raise Exception("couldn't find any appropriate .pkl files in the CWD")
restore_fn = files[-1]
dont_train = False
verbose_summary = False
reslayers = 4
size = 8
batch_size = 128
epochs = 24
convolutional = True
resnet_enabled = True
original_resnet = False
LR = 1e-2
LRprod = 0.1**(1/20.) # will use a tenth of the learning rate after 20 epochs
use_image_generator = True
def prepare(X, y):
X = X.reshape(X.shape[0], 1, width, height).astype('float32') / 255
# convert class vectors to binary class matrices
Y = to_categorical(y, nb_classes)
return X, Y
# the data, shuffled and split between train and test sets
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train, Y_train = prepare(X_train, y_train)
X_test, Y_test = prepare(X_test, y_test)
if use_image_generator:
from keras.preprocessing.image import ImageDataGenerator
idg = ImageDataGenerator(rotation_range=5.,
width_shift_range=.10,
height_shift_range=.10,
shear_range=5 / 180 * np.pi,
zoom_range=0.1,
fill_mode='constant',
cval=0.)
# ReLU activation is supposed to be the best with he_normal
if convolutional:
layer = lambda x: Convolution2D(x, 3, 3, init='he_normal', border_mode='same')
else:
layer = lambda x: Dense(x, init='he_normal')
# start construting the model
x = Input(shape=(1, width, height))
y = x
if convolutional:
# it might be worth trying other sizes here
y = Convolution2D(size, 7, 7, subsample=(2, 2), border_mode='same')(y)
y = MaxPooling2D()(y)
else:
y = Flatten()(y)
y = Dense(dense_size)(y)
for i in range(reslayers):
skip = y
if original_resnet:
y = layer(size)(y)
y = BatchNormalization(axis=1)(y)
y = Activation('relu')(y)
y = layer(size)(y)
y = BatchNormalization(axis=1)(y)
if resnet_enabled: y = merge([skip, y], mode='sum')
y = Activation('relu')(y)
else:
y = BatchNormalization(axis=1)(y)
y = Activation('relu')(y)
y = layer(size)(y)
y = BatchNormalization(axis=1)(y)
y = Activation('relu')(y)
y = layer(size)(y)
if resnet_enabled: y = merge([skip, y], mode='sum')
if convolutional:
from keras.layers import AveragePooling1D
y = Reshape((size, int(width * height / 2**2 / 2**2)))(y)
y = AveragePooling1D(size)(y)
y = Flatten()(y)
y = Dense(nb_classes)(y)
y = Activation('softmax')(y)
model = Model(input=x, output=y)
if verbose_summary:
model.summary()
else:
total_params = 0
for layer in model.layers:
total_params += layer.count_params()
print("Total params: {}".format(total_params))
if restore_fn:
with open(restore_fn, 'rb') as f:
W = pickle.loads(f.read())
if not dont_train:
# sparsify an existing model
for i, w in enumerate(W):
if w.shape == (size, size, 3, 3):
middle = np.median(np.abs(w.flat))
where = np.abs(w) < middle
total = np.prod(w.shape)
fmt = 'W[{}]: zeroing {} params of {}'
print(fmt.format(i, int(np.count_nonzero(where)), int(total)))
W[i] = np.where(where, 0, w)
model.set_weights(W)
LR /= 10
model.compile(loss=loss, optimizer='adam', metrics=['accuracy'])
if not dont_train:
callbacks = [LearningRateScheduler(lambda e: LR * LRprod**e)]
kwargs = dict(
nb_epoch=epochs,
validation_data=(X_test, Y_test),
callbacks=callbacks,
verbose=1
)
if use_image_generator:
history = model.fit_generator(idg.flow(X_train, Y_train, batch_size=batch_size),
samples_per_epoch=len(X_train), **kwargs)
else:
history = model.fit(X_train, Y_train, batch_size=batch_size,
**kwargs)
def evaluate(X, Y):
score = model.evaluate(X, Y, verbose=0)
for name, score in zip(model.metrics_names, score):
if name == "acc":
print("{:7} {:6.2f}%".format(name, score * 100))
else:
print("{:7} {:7.5f}".format(name, score))
print('TRAIN')
evaluate(X_train, Y_train)
print('TEST')
evaluate(X_test, Y_test)
print('ALL')
evaluate(np.vstack((X_train, X_test)), np.vstack((Y_train, Y_test)))
if not dont_train:
open(name+'.json', 'w').write(model.to_json())
with open(name+'.pkl', 'wb') as f:
f.write(pickle.dumps(model.get_weights()))