diff --git a/synchrod/README b/synchrod/README new file mode 100644 index 0000000..e264b28 --- /dev/null +++ b/synchrod/README @@ -0,0 +1,12 @@ +This is a copy of the Python scripts for the TI-Planet and Planete Casio +back to school 2021 contest. + +The original code can be found at: + +https://tiplanet.org/forum/viewtopic.php?f=49&t=25200 + +The demonstration script can be executed with the following commands: + + g++ -O3 -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` astar.cpp -o astar`python3-config --extension-suffix` + + python3 ia.py diff --git a/synchrod/astar.cpp b/synchrod/astar.cpp new file mode 100644 index 0000000..dfb8636 --- /dev/null +++ b/synchrod/astar.cpp @@ -0,0 +1,118 @@ +/* +compilation command: +g++ -O3 -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` astar.cpp -o astar`python3-config --extension-suffix` +*/ + +#include +#include +#include +#include + +#include +#include + +#define MAX(a,b) (((a)>(b))?(a):(b)) + +namespace py = pybind11; + +using namespace std; + +int deltas[4] = {-16, 16, -1, 1}; + +struct result +{ + double cost; + int players; +}; + +struct result cost(const vector &t, int players, int exits, int m, int l) +{ + int i, n, dn, p, e, mask; + double c, v; + struct result r; + p = players; + dn = deltas[m]; + c = 1; + for(i = 0; i < 4; ++i) + { + n = p >> i * 8 & 0xff; + e = exits >> i * 8 & 0xff; + if(n == e) continue; + n += dn; + v = t[n + i * l]; + if(v < 0) continue; + mask = 0xff << i * 8; + p = (p & ~mask) | (n << i * 8); + c += v; + } + r.cost = c; + r.players = p; + return r; +} + +double heuristic(int u, int d) +{ + int result, dx[4], i; + for(i = 0; i < 4; ++i) + { + dx[i] = abs(u >> i * 8 & 0xff) % 16 - abs(d >> i * 8 & 0xff) % 16 ; + } + return MAX(dx[0], dx[2]) + MAX(dx[1], dx[3]); +} + +vector astar(const vector &t, int s, int d, int l) +{ + int m, n, u; + double c, cu; + struct result r; + vector path; + typedef pair element; + priority_queue, greater > q; + unordered_map prev, move; + unordered_map dist; + + q.emplace(0, s); + prev[s] = s; + dist[s] = 0; + move[s] = 0; + + while(!q.empty()) + { + u = q.top().second; + q.pop(); + if(u == d) break; + cu = dist[u]; + for(m = 0; m < 4; ++m) + { + r = cost(t, u, d, m, l); + c = r.cost; + n = r.players; + if(u == n) continue; + c += cu; + if(dist.find(n) == dist.end() || c < dist[n]) + { + q.emplace(c + heuristic(n, d), n); + prev[n] = u; + dist[n] = c; + move[n] = m; + } + } + } + + if(u == d) + { + while(u != s) + { + path.push_back(move[u]); + u = prev[u]; + } + reverse(path.begin(), path.end()); + } + + return path; +} + +PYBIND11_MODULE(astar, m) +{ + m.def("astar", &astar, ""); +} diff --git a/synchrod/ia.py b/synchrod/ia.py new file mode 100644 index 0000000..ad4d73e --- /dev/null +++ b/synchrod/ia.py @@ -0,0 +1,54 @@ +from synchrod import * + +from astar import astar + +def convert(board): + t = [] + for i in range(4): + for c in board: + if c == WALL or (is_a(c, EXIT) and c != EXIT + i): v = -1 + elif c == EXIT + i or not affects(c, i): v = 0 + elif is_a(c, MONSTER): v = 1 + elif is_a(c, SPIKES): v = 10 + elif is_a(c, TRAP): v = 10 / 3 + t.append(v) + return t + +def hash(p): + return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0]; + +moves = [GO_UP, GO_DOWN, GO_LEFT, GO_RIGHT] + +deltas = [-16, +16, -1, +1] + +path = [] + +def turn(board, players, events): + global path + + sleep(50) + + if {NEW_GAME, TRAP_APPEARED, SPIKES_APPEARED}.intersection([e[2] for e in events]): + t = convert(board) + p = players.copy() + e = [0, 0, 0, 0] + l = len(board) + for i in range(4): + e[i] = board.index(EXIT + i) + if p[i] < 0: p[i] = e[i] + path = astar(t, hash(p), hash(e), l) + + m = moves[path[0]] + dn = deltas[path[0]] + for i in range(4): + n = players[i] + if n < 0: continue + dest = board[n + dn] + if est_un(dest, MONSTER) and affects(dest, i): + return ATTACK + + path = path[1:] + + return m + +play_game(turn, blind = False) diff --git a/synchrod/polycal4.py b/synchrod/polycal4.py new file mode 100644 index 0000000..74b8956 --- /dev/null +++ b/synchrod/polycal4.py @@ -0,0 +1,511 @@ +#cas +from math import pi, asin + +KEY_NONE, KEY_LEFT, KEY_UP, KEY_DOWN, KEY_RIGHT, KEY_ENTER, KEY_ESC, KEY_7, KEY_8, KEY_9, KEY_LEFT_PARENTHESIS, KEY_RIGHT_PARENTHESIS, KEY_4, KEY_5, KEY_6, KEY_1, KEY_2, KEY_3, KEY_0 = 0, 1, 2, 3, 4, 53, 6, 30, 31, 32, 34, 35, 36, 37, 38, 42, 43, 44, 48 + +def fix_angle(a): + return a * 2 * asin(1) / pi +def col3_2_rgb(c, bits, bgr): + return c[2*bgr]//2**(8 - bits[0]) + c[1]//2**(8 - bits[1])*2**bits[0] + c[2*(not(bgr))]//2**(8-bits[2])*2**(bits[0] + bits[1]) + +def hp_draw_line(x1, y1, x2, y2, c): + line_p(x1, y1, x2, y2, col3_2_rgb(c, [8, 8, 8], 1)) +def hp_fill_rect(x, y, w, h, c): + rect_p(x, y, x + w - 1, y + h - 1, col3_2_rgb(c, [8, 8, 8], 1)) +def hp_draw_circle(x, y, r, c): + global L1 + c = col3_2_rgb(c, [8, 8, 8], 1) + arc_p(x, y, r, 0, fix_angle(2*pi), c) +def hp_fill_circle(x, y, r, c): + global L1, L2 + c = col3_2_rgb(c, [8, 8, 8], 1) + L2 = [c, c] + arc_p(x, y, r, 0, fix_angle(2*pi), L2) +def hp_draw_ellipse(x, y, rx, ry, c): + global L1 + c, L1 = col3_2_rgb(c, [8, 8, 8], 1), [rx, ry] + arc_p(x, y, L1, 0, fix_angle(2*pi), c) +def hp_fill_ellipse(x, y, rx, ry, c): + global L1, L2 + c, L1 = col3_2_rgb(c, [8, 8, 8], 1), [rx, ry] + L2 = [c, c] + arc_p(x, y, L1, 0, fix_angle(2*pi), L2) +def hp_draw_string(s, x, y, cf, cb=(255,255,255)): + textout_p(s, x, y, 1, col3_2_rgb(cf, [8, 8, 8], 1), len(s) * 9, col3_2_rgb(cb, [8, 8, 8], 1)) +def hp_clear_screen(): + rect_p(0, 0, 319, 239, 16777215) +def hp_pause(): + wait() +def hp_sleep(d): + wait(d) +def hp_monotonic(d): + return time() +def hp_get_key(): + d_key = {KEY_LEFT:7, KEY_UP:2, KEY_DOWN:12, KEY_RIGHT:8, KEY_ENTER:30, KEY_ESC:4, KEY_LEFT_PARENTHESIS:28, KEY_0:47, KEY_1:42, KEY_2:43, KEY_3:44, KEY_4:37, KEY_5:38, KEY_6:39, KEY_7:32, KEY_8:33, KEY_9:34 } + key = get_key() + return key in d_key.values() and list(d_key.keys())[list(d_key.values()).index(key)] + +def kc1_draw_line(x1, y1, x2, y2, c): + draw_line(float(x1), float(y1), float(x2), float(y2), not(col3_2_rgb(c, [1, 1, 1], 0))) +def kc1_fill_rect(x, y, w, h, c): + draw_rectangle(float(x), float(y), float(w), float(h), (not (col3_2_rgb(c, [1, 1, 1], 0))) + filled) +def kc1_draw_circle(x, y, r, c): + draw_arc(float(x), float(y), float(r), not (col3_2_rgb(c, [1, 1, 1], 0))) +def kc1_fill_circle(x, y, r, c): + draw_arc(float(x), float(y), float(r), (not (col3_2_rgb(c, [1, 1, 1], 0))) + filled) +def kc1_draw_ellipse(x, y, rx, ry, c): + draw_arc(float(x), float(y), float(rx), float(ry), 0, 2*pi, not (col3_2_rgb(c, [1, 1, 1], 0))) +def kc1_fill_ellipse(x, y, rx, ry, c): + draw_arc(float(x), float(y), float(rx), float(ry), 0, 2*pi, (not (col3_2_rgb(c, [1, 1, 1], 0))) + filled) +def kc1_draw_string(s, x, y, cf, cb=(255,255,255)): + draw_string(s, float(x), float(y), col3_2_rgb(cf, [1, 1, 1], 1), col3_2_rgb(cb, [1, 1, 1], 1)) +def kc_get_key(): + d_key = {KEY_LEFT:0, KEY_UP:1, KEY_DOWN:2, KEY_RIGHT:3, KEY_ENTER:30004, KEY_ESC:5, KEY_LEFT_PARENTHESIS:40, KEY_RIGHT_PARENTHESIS:41, KEY_0:48, KEY_1:49, KEY_2:50, KEY_3:51, KEY_4:52, KEY_5:53, KEY_6:54, KEY_7:55, KEY_8:56, KEY_9:57 } + key = get_key() + return key in d_key.values() and list(d_key.keys())[list(d_key.values()).index(key)] + +tcanvas = None + +def get_infos(keys, sdl_width=640, sdl_height=480, sdl_scale=1): + global hp_draw_line, hp_fill_rect, hp_draw_circle, hp_fill_circle, hp_draw_ellipse, hp_fill_ellipse, hp_draw_string, kc1_draw_line, kc1_fill_rect, kc1_draw_ellipse, kc1_fill_ellipse, kc1_draw_string, hp_clear_screen, hp_pause, tcanvas + fnop = lambda : None + ffalse = lambda : False + screen_w, screen_h, screen_y0, color_bits, font_w, font_num_w, font_h, poly_has_color, poly_get_pixel, poly_set_pixel, poly_show, poly_draw_line, poly_fill_rect, poly_draw_circle, poly_fill_circle, poly_draw_ellipse, poly_fill_ellipse, poly_monotonic, poly_sleep, poly_draw_string, poly_clear_screen, poly_pause, poly_set_buffer, poly_get_key, poly_wait_key, poly_wait_release, poly_test_key, poly_esc_key, show_need_pause_on_exit, screen_need_clear_on_init, need_clear, need_line, need_rect, need_ellipse, need_string, need_monotonic, need_sleep, has_keys = 0, 0, 0, [5, 6, 5], 0, 0, 0, 1, fnop, fnop, fnop, fnop, fnop, fnop, fnop, fnop, fnop, fnop, fnop, fnop, fnop, fnop, fnop, fnop, ffalse, ffalse, ffalse, ffalse, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1 + if not (screen_w): + try: + if chr(256)==chr(0): # KhiCAS Python compat. + need_line, need_rect, need_ellipse, need_string, need_sleep, need_monotonic = 0, 0, 0, 0, 0, 0 + if "HP" in version(): + kc1_draw_line, kc1_fill_rect, kc1_draw_circle, kc1_fill_circle, kc1_draw_ellipse, kc1_fill_ellipse, kc1_draw_string = None, None, None, None, None, None, None + screen_w, screen_h, font_w, font_h, show_need_pause_on_exit = 320, 240, 9, 11, 1 + font_num_w = font_w + def poly_set_pixel(x, y, c): + set_pixel(x, y, col3_2_rgb(c, [8, 8, 8], 1)) + poly_draw_line, poly_fill_rect, poly_draw_circle, poly_fill_circle, poly_draw_ellipse, poly_fill_ellipse, poly_draw_string, poly_clear_screen, poly_pause, poly_sleep, poly_monotonic = hp_draw_line, hp_fill_rect, hp_draw_circle, hp_fill_hp_draw_circle, hp_draw_ellipse, hp_fill_ellipse, hp_draw_string, hp_clear_screen, hp_pause, hp_sleep, hp_monotonic + else: # Graph 35+E II / NumWorks or Nspire / Graph 90+E + hp_draw_line, hp_fill_rect, hp_draw_string, hp_clear_screen, hp_pause = None, None, None, None, None + t_mono, t_nw, t_ns, need_clear = not (white), "Numworks" in version(), "Nspire" in version(), 1 + screen_w, screen_h, font_w, font_h, poly_get_pixel, poly_pause, poly_sleep = 384 - (t_ns or t_nw)*64 - t_mono*256, 192 + (t_ns or t_nw)*30 - t_mono*128, get_pixel, 10 - 4*t_mono + 5*t_ns + t_nw, 10 - 5*t_mono + 8*(t_ns or t_nw), getKey, sleep + font_num_w = font_w + def poly_monotonic(): + return monotonic / (1 + (not(t_nw or t_ns))*99) + key_d = {KEY_LEFT:0, KEY_UP:1, KEY_DOWN:2, KEY_RIGHT:3, KEY_ENTER:4, KEY_ESC:5, KEY_LEFT_PARENTHESIS:40, KEY_RIGHT_PARENTHESIS:41} + def poly_wait_key(): + key = get_key() + for k in key_d: + if key == key_d[k]: + return k + return 0 + if t_mono: + def poly_set_pixel(x, y, c): + set_pixel(x, y, not(col3_2_rgb(c, [1,1,1], 0))) + poly_draw_line, poly_fill_rect, poly_draw_circle, poly_fill_circle, poly_draw_ellipse, poly_fill_ellipse, poly_draw_string = kc1_draw_line, kc1_fill_rect, kc1_draw_circle, kc1_fill_circle, kc1_draw_ellipse, kc1_fill_ellipse, kc1_draw_string + else: + kc1_draw_line, kc1_fill_rect, kc1_draw_circle, kc1_fill_circle, kc1_draw_ellipse, kc1_fill_ellipse, kc1_draw_string = None, None, None, None, None, None, None + def poly_set_pixel(x, y, c): + set_pixel(x, y, col3_2_rgb(c, [5,6,5], 0)) + def poly_draw_line(x1, y1, x2, y2, c): + draw_line(float(x1), float(y1), float(x2), float(y2), col3_2_rgb(c, color_bits, 1)) + def poly_fill_rect(x, y, w, h, c): + draw_rectangle(float(x), float(y), float(w), float(h), col3_2_rgb(c, color_bits, 1) + filled) + def poly_draw_circle(x, y, r, c): + draw_arc(float(x), float(y), float(r), float(r), 0, 2*pi, col3_2_rgb(c, color_bits, 1)) + def poly_fill_circle(x, y, r, c): + draw_arc(float(x), float(y), float(r), float(r), 0, 2*pi, col3_2_rgb(c, color_bits, 1) + filled) + def poly_draw_ellipse(x, y, rx, ry, c): + draw_arc(float(x), float(y), float(rx), float(ry), 0, 2*pi, col3_2_rgb(c, color_bits, 1)) + def poly_fill_ellipse(x, y, rx, ry, c): + draw_arc(float(x), float(y), float(rx), float(ry), 0, 2*pi, col3_2_rgb(c, color_bits, 1) + filled) + def poly_draw_string(s, x, y, cf, cb=(255,255,255)): + draw_string(s, float(x), float(y), col3_2_rgb(cf, color_bits, 1), col3_2_rgb(cb, color_bits, 1)) + except: + pass + if not (screen_w): + hp_draw_line, hp_fill_rect, hp_draw_circle, hp_fill_circle, hp_draw_ellipse, hp_fill_ellipse, hp_draw_string, kc1_draw_line, kc1_fill_rect, kc1_draw_circle, kc1_fill_circle, kc1_draw_ellipse, kc1_fill_ellipse, kc1_draw_string, hp_clear_screen, hp_pause = None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None + try: + import sys + try: + t_nw = sys.platform == "numworks" + if sys.platform == "nspire" or t_nw: + try: # KhiCAS Micropython + import graphic, nsp, cas, time + def poly_draw_line(x1, y1, x2, y2, c): + graphic.draw_line(int(x1), int(y1), int(x2), int(y2), col3_2_rgb(c, color_bits, 1)) + def poly_fill_rect(x1, y1, x2, y2, c): + graphic.draw_filled_rectangle(int(x1), int(y1), int(x2), int(y2), c) + def poly_draw_circle(x, y, r, c): + graphic.draw_circle(int(x), int(y), int(r), c) + def poly_fill_circle(x, y, r, c): + graphic.draw_filled_circle(int(x), int(y), int(r), c) + def poly_draw_ellipse(x, y, rx, ry, c): + graphic.draw_arc(int(x), int(y), int(rx), int(ry), 0, 360, c) + def poly_fill_ellipse(x, y, rx, ry, c): + graphic.draw_filled_arc(int(x), int(y), int(rx), int(ry), 0, 360, c) + def poly_draw_string(s, x, y, cf, cb=(255,255,255)): + cas.caseval('draw_string("' + s + '", ' + str(float(x)) + ', ' + str(float(y)) + ', ' + str(col3_2_rgb(cf, color_bits, 1)) + ', ' + str(col3_2_rgb(cb, color_bits, 1)) + ')') + def poly_wait_key(): + d_key = {KEY_LEFT:0, KEY_UP:1, KEY_DOWN:2, KEY_RIGHT:3, KEY_ENTER:4, KEY_ESC:5, KEY_LEFT_PARENTHESIS:40, KEY_RIGHT_PARENTHESIS:41, KEY_0:48, KEY_1:49, KEY_2:50, KEY_3:51, KEY_4:52, KEY_5:53, KEY_6:54, KEY_7:55, KEY_8:56, KEY_9:57} + key = int(cas.caseval("get_key()")) + return key in d_key.values() and list(d_key.keys())[list(d_key.values()).index(key)] + def poly_monotonic(): + return time.monotonic() / (1 + t_nw*999) + screen_w, screen_h, font_w, font_h, poly_get_pixel, poly_set_pixel, poly_show, poly_pause, need_clear, need_line, need_rect, need_ellipse, need_string, need_monotonic, screen_need_clear_on_init = 320, 222, 15, 18, graphic.get_pixel, graphic.set_pixel, graphic.show_screen, nsp.waitKeypress, 1, 0, 0, 0, 0, 0, 1 + font_num_w = font_w + except: # Nspire MicroPython + import nsp + screen_w, screen_h, need_clear, show_need_pause_on_exit = 320, 240, 1, 1 + tcanvas = nsp.Texture(screen_w, screen_h, 0) + poly_get_pixel = tcanvas.getPx + def poly_set_pixel(x, y, c): + if x>=0 and x<=screen_w-1 and y>=0 and y <=screen_h-1: + tcanvas.setPx(x, y, col3_2_rgb(c, color_bits, 1)) + poly_show, poly_pause = tcanvas.display, nsp.waitKeypress + elif sys.platform == "TI-Nspire": # CX II + import time + screen_w, screen_h, font_w, font_h, need_monotonic, need_sleep, poly_sleep = 318, 212, 12, 12, 0, 0, time.sleep + font_num_w = font_w + def test(d,f): + t=f() + time.sleep(d) + return round((f()-t)/d,0) + if test(0.1, time.clock) == 1: + poly_monotonic = time.clock + else: + poly_monotonic = time.time + try: + import ti_system + d_key = {KEY_LEFT:'left', KEY_UP:'up', KEY_DOWN:'down', KEY_RIGHT:'right', KEY_ENTER:'enter', KEY_ESC:'esc', KEY_LEFT_PARENTHESIS:'(', KEY_RIGHT_PARENTHESIS:')', KEY_0:'0', KEY_1:'1', KEY_2:'2', KEY_3:'3', KEY_4:'4', KEY_5:'5', KEY_6:'6', KEY_7:'7', KEY_8:'8', KEY_9:'9'} + def poly_get_key(): + key = ti_system.get_key() + return key in d_key.values() and list(d_key.keys())[list(d_key.values()).index(key)] + def poly_wait_key(): + key = "" + while key == "": + key = ti_system.get_key() + return key in d_key.values() and list(d_key.keys())[list(d_key.values()).index(key)] + def poly_test_key(v): + return poly_get_key() == d_key[v] + def poly_esc_key(): + return poly_test_key(KEY_ESC) + except: + has_keys = 0 + if "gp" in keys: + import ti_image + need_line, need_rect, need_ellipse, need_string = 1, 1, 1, 1 + tcanvas = ti_image.new_image(screen_w, screen_h, (255,255,255)) + poly_get_pixel, poly_set_pixel = tcanvas.get_pixel, tcanvas.set_pixel + def poly_show(): + tcanvas.show_image(0,0) + else: + import ti_draw + ti_draw.use_buffer() + ti_draw.set_pen("thin","solid") + need_line, need_rect, need_ellipse, need_string, poly_show, set_buffer = 0, 0, 0, 0, ti_draw.paint_buffer, ti_draw.use_buffer + def poly_set_pixel(x, y, c): + ti_draw.set_color(tuple(c)) + ti_draw.plot_xy(x, y, 7) + def poly_draw_line(x1, y1, x2, y2, c): + ti_draw.set_color(tuple(c)) + ti_draw.draw_line(x1, y1, x2, y2) + def poly_fill_rect(x, y, w, h, c): + ti_draw.set_color(tuple(c)) + ti_draw.fill_rect(x, y, w, h) + def poly_draw_circle(x, y, r, c): + ti_draw.set_color(tuple(c)) + ti_draw.draw_circle(x, y, r) + def poly_fill_circle(x, y, r, c): + ti_draw.set_color(tuple(c)) + ti_draw.fill_circle(x, y, r) + def poly_draw_ellipse(x, y, rx, ry, c): + ti_draw.set_color(tuple(c)) + x0, y0 = x - rx, y - ry + for dy in range(1 + (y0 > int(y0))): + for dx in range(1 + (x0 > int(x0))): + ti_draw.draw_arc(x0 + dx, y0 + dy, 2 * rx, 2 * ry, 0, 360) + def poly_fill_ellipse(x, y, rx, ry, c): + ti_draw.set_color(tuple(c)) + ti_draw.fill_arc(x - rx, y - ry, 2 * rx, 2 * ry, 0, 360) + def poly_draw_string(s, x, y, cf, cb=(255,255,255)): + poly_fill_rect(x, y, font_w * len(s), font_h, cb) + ti_draw.set_color(tuple(cf)) + ti_draw.draw_text(x, y + font_h, s) + elif sys.platform.startswith('TI-Python'): + import ti_graphics, ti_system, time + screen_w, screen_h, font_w, font_h, screen_y0, need_line, need_rect, need_ellipse, need_string, need_monotonic, need_sleep, poly_pause, poly_wait_key, poly_sleep, poly_monotonic, poly_esc_key, need_clear, show_need_pause_on_exit, need_sleep, screen_need_clear_on_init = 320, 210, 10, 15, 30, 0, 0, 0, 0, 0, ti_system.disp_wait, ti_system.wait_key, time.sleep, time.monotonic, ti_system.escape, 1, 1, 0, 1 + font_num_w = font_w + def poly_draw_line(x1, y1, x2, y2, c): + ti_graphics.setColor(c) + ti_graphics.drawLine(x1, y1 + screen_y0, x2, y2 + screen_y0) + def poly_fill_rect(x, y, w, h, c): + ti_graphics.setColor(c) + ti_graphics.fillRect(x, y + screen_y0, w, h) + def poly_get_pixel(x, y): + return ti_graphics.getPixel(x, y + screen_y0) + def poly_set_pixel(x, y, c): + ti_graphics.setPixel(x, y + screen_y0, c) + def poly_draw_ellipse(x, y, rx, ry, c): + ti_graphics.setColor(c) + x0, y0 = x - rx, y - ry + for dy in range(1 + (y0 > int(y0))): + for dx in range(1 + (x0 > int(x0))): + ti_graphics.drawArc(x0 + dx, y0 + dy + screen_y0, 2 * rx, 2 * ry, 0, 3600) + def poly_fill_ellipse(x, y, rx, ry, c): + ti_graphics.setColor(c) + ti_graphics.fillArc(x - rx, y - ry + screen_y0, 2 * rx, 2 * ry, 0, 3600) + def poly_draw_circle(x, y, r, c): + poly_draw_ellipse(x, y, r, r, c) + def poly_fill_circle(x, y, r, c): + ti_graphics.setColor(c) + ti_graphics.fillCircle(x, y + screen_y0, r) + def poly_draw_string(s, x, y, cf, cb=(255,255,255)): + poly_fill_rect(x, y, font_w * len(s), font_h, cb) + ti_graphics.setColor(cf) + ti_graphics.drawString(s, x, y + screen_y0) + def poly_wait_key(): + d_key = {KEY_LEFT:2, KEY_UP:3, KEY_DOWN:4, KEY_RIGHT:1, KEY_ENTER:5, KEY_ESC:9, KEY_LEFT_PARENTHESIS:133, KEY_RIGHT_PARENTHESIS:134, KEY_0:142, KEY_1:143, KEY_2:144, KEY_3:145, KEY_4:146, KEY_5:147, KEY_6:148, KEY_7:149, KEY_8:150, KEY_9:151} + key = ti_system.wait_key() + return key in d_key.values() and list(d_key.keys())[list(d_key.values()).index(key)] + except: + pass + except: + pass + if not (screen_w): # Casio Graph 90/35+E II + try: + import casioplot + casioplot.set_pixel(0, 0, (0, 0, 255)) + col = casioplot.get_pixel(0, 0) + t = col[0] == col[2] + screen_w, screen_h, poly_has_color, font_w, font_h, poly_get_pixel, poly_set_pixel, poly_show, poly_clear_screen, need_string, has_keys = t and 128 or 384, t and 64 or 192, not (t), t and 6 or 10, t and 5 or 10, casioplot.get_pixel, casioplot.set_pixel, casioplot.show_screen, casioplot.clear_screen, 0, 0 + font_num_w = t and 4 or 8 + def poly_draw_string(s, x, y, cf, cb=(255,255,255)): + poly_fill_rect(x, y, font_w * len(s), font_h, cb) + casioplot.draw_string(x, y, s, cf, "small") + def poly_pause(): + try: + while(1): + pass + except KeyboardInterrupt: + pass + except: + try: # NumWorks + import kandinsky, ion, time + screen_w, screen_h, font_w, font_h, poly_get_pixel, poly_set_pixel, poly_draw_string, poly_sleep, poly_monotonic, need_rect, need_string, need_monotonic, need_sleep = 320, 222, 11, 18, kandinsky.get_pixel, kandinsky.set_pixel, kandinsky.draw_string, time.sleep, time.monotonic, 0, 0, 0, 0 + font_num_w = font_w + def poly_fill_rect(x, y, w, h, c): + kandinsky.fill_rect(int(x), int(y), int(w), int(h), c) + def poly_pause(): + def key_down(): + for k in range(53): + if ion.keydown(k): + return 1 + return 0 + while key_down(): + pass + while not (key_down()): + pass + def poly_draw_string(s, x, y, cf, cb=(255,255,255)): + kandinsky.draw_string(s, x, y, cf, cb) + d_key = {KEY_LEFT:0, KEY_UP:1, KEY_RIGHT:2, KEY_DOWN:3, KEY_ENTER:52, KEY_ESC:5, KEY_LEFT_PARENTHESIS:33, KEY_RIGHT_PARENTHESIS:34, KEY_0:48, KEY_1:42, KEY_2:43, KEY_3:44, KEY_4:36, KEY_5:37, KEY_6:38, KEY_7:30, KEY_8:31, KEY_9:32} + def poly_get_key(): + for k in d_key: + if poly_test_key(d_key[k]): + return k + return 0 + def poly_wait_key(): + k = 0 + while not k: + k = poly_get_key() + return k + def poly_test_key(v): + return ion.keydown(d_key[v]) + def poly_esc_key(): + return poly_test_key(KEY_ESC) + except: + try: # HP Prime + import hpprime + screen_w, screen_h, font_h, color_bits, need_clear, show_need_pause_on_exit, screen_need_clear_on_init, need_line, need_rect, need_string = 320, 240, 15, (8, 8, 8), 1, 1, 1, 0, 0, 0 + hpprime.dimgrob(1, screen_w, screen_h, 0) + def poly_set_pixel(x, y, c): + hpprime.pixon(1, x, y, col3_2_rgb(c, color_bits, 1)) + def poly_draw_line(x1, y1, x2, y2, c): + hpprime.line(1, x1, y1, x2, y2, col3_2_rgb(c, color_bits, 1)) + def poly_fill_rect(x, y, w, h, c): + hpprime.fillrect(1, x, y, w, h, col3_2_rgb(c, color_bits, 1), col3_2_rgb(c, color_bits, 1)) + def poly_show(): + hpprime.strblit(0, 0, 0, screen_w, screen_h, 1) + def poly_pause(): + while hpprime.keyboard(): pass + while not(hpprime.keyboard()): pass + def poly_draw_string(s, x, y, cf, cb=(255,255,255)): + sz = hpprime.eval('TEXTSIZE("'+s+'")') + poly_fill_rect(x, y, sz[0], sz[1], cb) + hpprime.textout(1, x, y, s, col3_2_rgb(cf, color_bits, 1)) + d_key = {KEY_LEFT:7, KEY_UP:2, KEY_DOWN:12, KEY_RIGHT:8, KEY_ENTER:30, KEY_ESC:4, KEY_LEFT_PARENTHESIS:28, KEY_RIGHT_PARENTHESIS:28, KEY_0:47, KEY_1:42, KEY_2:43, KEY_3:44, KEY_4:37, KEY_5:38, KEY_6:39, KEY_7:32, KEY_8:33, KEY_9:34 } + def poly_get_key(): + for k in d_key: + if poly_test_key(k): + return k + return 0 + def poly_wait_key(): + while not hpprime.keyboard(): pass + return poly_get_key() + def poly_wait_release(): + while hpprime.keyboard(): pass + def poly_test_key(v): + return hpprime.keyboard() & (1 << d_key[v]) + def poly_esc_key(): + return poly_test_key(KEY_ESC) + except: # computer + import polycalc_sdl2 + polycalc_sdl2.polycalc_pc_init(sdl_width, sdl_height, sdl_scale) + screen_w, screen_h, poly_set_pixel, poly_fill_rect, poly_clean_screen, poly_show, poly_pause, poly_wait_key, poly_get_key, poly_test_key, need_line, need_string = polycalc_sdl2.screen_w, polycalc_sdl2.screen_h, polycalc_sdl2.poly_set_pixel, polycalc_sdl2.poly_fill_rect, polycalc_sdl2.poly_clean_screen, polycalc_sdl2.poly_show, polycalc_sdl2.poly_pause, polycalc_sdl2.poly_wait_key, polycalc_sdl2.poly_get_key, polycalc_sdl2.poly_test_key, 1, 1 + color_bits = [8, 8, 8] + poly_has_color = 1 + show_need_pause_on_exit = 0 + + if screen_w: + if need_line: + def poly_draw_line(x1, y1, x2, y2, c): + m, a1, b1, a2, b2 = 0, int(x1), int(y1), int(x2), int(y2) + if (x2 - x1) ** 2 < (y2 - y1) ** 2: + m, a1, a2, b1, b2 = 1, b1, b2, a1, a2 + if min(a1, a2) != a1: a1, b1, a2, b2 = a2, b2, a1, b1 + for a in range(a1, a2 + 1): + b = int(b1 + (b2 - b1) * (a - a1) / ((a2 - a1) or 1)) + poly_set_pixel((a, b)[m], (b, a)[m], c) + + if need_rect: + def poly_fill_rect(x, y, w, h, c): + for k in range(h): + poly_draw_line(x, y + k, x + w - 1, y + k, c) + + if need_ellipse: + from math import sqrt + def poly_draw_ellipse(x, y, rx, ry, c): + for h in range(-int(ry), int(ry)+1): + w = sqrt(max(0, rx*rx*(1-h*h/ry/ry))) + x1, x2 = int(x - w), int(x + w) + yc = int(y + h) + poly_set_pixel(x1, yc, c) + poly_set_pixel(x2, yc, c) + for w in range(-int(rx), int(rx)+1): + h = sqrt(max(0, ry*ry*(1-w*w/rx/rx))) + y1, y2 = int(y - h), int(y + h) + xc = int(x + w) + poly_set_pixel(xc, y1, c) + poly_set_pixel(xc, y2, c) + + def poly_fill_ellipse(x, y, rx, ry, c): + for h in range(-int(ry), int(ry)+1): + w = sqrt(max(0, rx*rx*(1-h*h/ry/ry))) + poly_fill_rect(int(x - w), int(y + h), int(2 * w), 1, c) + def poly_draw_circle(x, y, r, c): + poly_draw_ellipse(x, y, r, r, c) + def poly_fill_circle(x, y, r, c): + poly_fill_ellipse(x, y, r, r, c) + + if need_string: + from polyfont import poly_font + font_w, font_h = 11, 10 + font_num_w = 9 + def poly_draw_string(s, x, y, cf, cb=(255,255,255)): + for c in s: + c = ord(c) >= 33 and ord(c) <= 127 and ord(c) - 32 or 0 + poly_fill_rect(x, y, font_w, font_h, cb) + for v in (0,) + poly_font[c] + (0,): + ty = y + while v: + if v & 1: + poly_set_pixel(x, ty, cf) + ty += 1 + v //= 2 + x += 1 + + if need_clear: + def poly_clear_screen(): + poly_fill_rect(0, 0, screen_w, screen_h, [255, 255, 255]) + + if screen_need_clear_on_init: + poly_clear_screen_init = poly_clear_screen + else: + poly_clear_screen_init = fnop + + if need_sleep: + def poly_sleep(d): + if need_monotonic: + for k in range(int(d*250000)): + pass + else: + s = poly_monotonic() + d + while poly_monotonic() < s: + pass + + if show_need_pause_on_exit: + def poly_show_exit(): + poly_show() + poly_pause() + else: + poly_show_exit = poly_show + + l = [] + for k in keys: + if k=="w": + l.append(screen_w) + if k=="h": + l.append(screen_h) + if k=="hc": + l.append(poly_has_color) + if k=="fw": + l.append(font_w) + if k=="fwn": + l.append(font_num_w) + if k=="fh": + l.append(font_h) + if k=="gp": + l.append(poly_get_pixel) + if k=="sp": + l.append(poly_set_pixel) + if k=="dl": + l.append(poly_draw_line) + if k=="fr": + l.append(poly_fill_rect) + if k=="dc": + l.append(poly_draw_circle) + if k=="fc": + l.append(poly_fill_circle) + if k=="de": + l.append(poly_draw_ellipse) + if k=="fe": + l.append(poly_fill_ellipse) + if k=="ds": + l.append(poly_draw_string) + if k=="cl": + l.append(poly_clear_screen) + if k=="cli": + l.append(poly_clear_screen_init) + if k=="sh": + l.append(poly_show) + if k=="she": + l.append(poly_show_exit) + if k=="p": + l.append(poly_pause) + if k=="sb": + l.append(poly_set_buffer) + if k=="m": + l.append(poly_monotonic) + if k=="s": + l.append(poly_sleep) + if k=="ek": + l.append(poly_esc_key) + if k=="gk": + l.append(poly_get_key) + if k=="wk": + l.append(poly_wait_key) + if k=="wr": + l.append(poly_wait_release) + if k=="tk": + l.append(poly_test_key) + if k=="hk": + l.append(has_keys) + return l + diff --git a/synchrod/polycalc_sdl2.py b/synchrod/polycalc_sdl2.py new file mode 100644 index 0000000..1629681 --- /dev/null +++ b/synchrod/polycalc_sdl2.py @@ -0,0 +1,150 @@ +from sdl2 import * +from ctypes import byref +import atexit + +from polycal4 import KEY_NONE, KEY_LEFT, KEY_UP, KEY_DOWN, KEY_RIGHT, \ + KEY_ENTER, KEY_ESC, KEY_LEFT_PARENTHESIS, KEY_RIGHT_PARENTHESIS + +window = None +renderer = None +texture = None + +pause_on_quit = False + +def polycalc_pc_init(width=0, height=0, scale=1): + if SDL_Init(SDL_INIT_VIDEO) < 0: + raise Exception("Failed to initialize SDL") + dm = SDL_DisplayMode() + SDL_GetCurrentDisplayMode(0, byref(dm)) + + global window + global renderer + global texture + global screen_w, screen_h, SCALE + if width <= 0: width = dm.w // SCALE + if height <= 0: height = dm.h // SCALE + screen_w, screen_h, SCALE = width, height, scale + window = SDL_CreateWindow("Polycalc SDL2".encode(), + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + width*SCALE, height*SCALE, SDL_WINDOW_SHOWN) + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED) + + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_TARGET, width, height) + + if window is None or renderer is None or texture is None: + raise Exception("Failed to create either window, renderer or texture") + + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, b'0') + SDL_SetRenderTarget(renderer, texture) + + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255) + SDL_RenderClear(renderer) + + atexit.register(polycalc_pc_quit) + +def poly_clean_screen(): + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255) + SDL_RenderClear(renderer) + +def poly_set_pixel(x, y, color): + SDL_SetRenderDrawColor(renderer, *color, 255) + SDL_RenderDrawPoint(renderer, x, y) + +def poly_show(): + global renderer + global texture + + SDL_SetRenderTarget(renderer, None) + SDL_RenderSetScale(renderer, SCALE, SCALE) + + SDL_RenderCopy(renderer, texture, None, None) + SDL_RenderPresent(renderer) + + SDL_SetRenderTarget(renderer, texture) + SDL_RenderSetScale(renderer, 1, 1) + +def poly_sdl2_screenshot(scale=-1, fname=None): + global renderer + global texture + + if scale == -1: + scale = SCALE + w = screen_w * scale + h = screen_h * scale + + if fname == None: + fname = b"poly_sdl2_screenshot.bmp" + fname = bytes(fname) + + surface = SDL_CreateRGBSurface(0, w, h, 32, 0, 0, 0, 0) + + SDL_SetRenderTarget(renderer, None) + SDL_RenderSetScale(renderer, scale, scale) + + SDL_RenderReadPixels(renderer, None, 0, surface.contents.pixels, + surface.contents.pitch) + SDL_SaveBMP(surface, fname) + + SDL_SetRenderTarget(renderer, texture) + SDL_RenderSetScale(renderer, 1, 1) + +def poly_pause(): + event = SDL_Event() + while 1: + SDL_WaitEvent(event) + if event.type == SDL_KEYDOWN: + break + if event.type == SDL_QUIT: + raise SystemExit + +def poly_fill_rect(x, y, w, h, color): + SDL_SetRenderDrawColor(renderer, *color, 255) + SDL_RenderFillRect(renderer, SDL_Rect(x, y, w, h)) + +_keys = { + KEY_LEFT: (SDLK_LEFT, SDL_SCANCODE_LEFT), + KEY_UP: (SDLK_UP, SDL_SCANCODE_UP), + KEY_DOWN: (SDLK_DOWN, SDL_SCANCODE_DOWN), + KEY_RIGHT: (SDLK_RIGHT, SDL_SCANCODE_RIGHT), + KEY_ENTER: (SDLK_RETURN, SDL_SCANCODE_RETURN), + KEY_ESC: (SDLK_ESCAPE, SDL_SCANCODE_ESCAPE), + KEY_LEFT_PARENTHESIS: (SDLK_LEFTPAREN, SDL_SCANCODE_5), + KEY_RIGHT_PARENTHESIS: (SDLK_RIGHTPAREN, SDL_SCANCODE_MINUS), +} + +def poly_wait_key(): + event = SDL_Event() + while 1: + SDL_WaitEvent(event) + if event.type == SDL_KEYDOWN: + for key, value in _keys.items(): + if event.key.keysym.sym == value[0]: + return key + +def poly_test_key(key): + st = SDL_GetKeyboardState(None) + return st[_keys[key][1]] != 0 + +def poly_get_key(): + for key in _keys: + if poly_test_key(key): + return key + return KEY_NONE + +def polycalc_pc_quit(): + global window + global renderer + global texture + + poly_show() + + if pause_on_quit: + poly_pause() + + SDL_SetRenderTarget(renderer, None) + SDL_DestroyTexture(texture) + SDL_DestroyRenderer(renderer) + SDL_DestroyWindow(window) + SDL_Quit() diff --git a/synchrod/polyfont.py b/synchrod/polyfont.py new file mode 100644 index 0000000..10a3e43 --- /dev/null +++ b/synchrod/polyfont.py @@ -0,0 +1,105 @@ +# font +# height : 10px +# width : variable +# margin-left : 1px +# margin-top : 0px +# margin-right : 1px +# margin-bottom : 0px + +poly_font = ( + (0,), + (0, 447, 0, 0), # ! + (11, 7, 0, 11, 7, 0), # " + (64, 452, 124, 455, 124, 71, 4), # # + (140, 274, 1023, 290, 196), # $ + (6, 393, 102, 24, 198, 288, 192), # % + (192, 302, 273, 297, 198, 288, 272), # & + (11, 7, 0), # ' + (120, 390, 513, 0), # ( + (513, 390, 120, 0), # ) + (68, 40, 254, 40, 68, 0), # * + (16, 16, 16, 254, 16, 16, 16), # + + (384, 896, 0), # , + (16, 16, 16, 16, 16, 16, 16), # - + (384, 384, 0), # . + (384, 96, 24, 6, 0), # / + (254, 257, 257, 257, 257, 254, 0), # 0 + (0, 258, 258, 511, 256, 256, 0), # 1 + (390, 321, 289, 289, 273, 398, 0), # 2 + (198, 257, 273, 273, 273, 238, 0), # 3 + (96, 88, 70, 321, 511, 320, 0), # 4 + (159, 265, 265, 265, 265, 241, 0), # 5 + (254, 273, 273, 273, 273, 226, 0), # 6 + (7, 1, 449, 49, 13, 3, 0), # 7 + (238, 273, 273, 273, 273, 238, 0), # 8 + (142, 273, 273, 273, 273, 254, 0), # 9 + (204, 204, 0), # : + (716, 460, 0), # ; + (16, 40, 68, 130, 0), # < + (40, 40, 40, 40, 40, 40, 40), # = + (130, 68, 40, 16, 0), # > + (6, 1, 433, 9, 6, 0), # ? + (124, 130, 313, 325, 381, 322, 60), # @ + (384, 112, 76, 67, 76, 112, 384), # A + (257, 511, 273, 273, 273, 273, 238), # B + (124, 130, 257, 257, 257, 130, 71), # C + (257, 511, 257, 257, 257, 130, 124), # D + (257, 511, 273, 273, 273, 313, 387), # E + (257, 511, 273, 17, 17, 57, 3), # F + (124, 130, 257, 273, 273, 146, 503), # G + (257, 511, 273, 16, 273, 511, 257), # H + (257, 257, 511, 257, 257), # I + (192, 256, 257, 257, 255, 1), # J + (257, 511, 273, 8, 309, 451, 257), # K + (257, 511, 257, 256, 256, 256, 448), # L + (257, 511, 262, 56, 192, 56, 262, 511, 257), # M + (257, 511, 262, 24, 97, 511, 1), # N + (124, 130, 257, 257, 257, 130, 124), # O + (257, 511, 273, 17, 17, 17, 14), # P + (124, 130, 321, 321, 321, 130, 380), # Q + (257, 511, 273, 17, 49, 209, 270), # R + (454, 137, 273, 273, 290, 199), # S + (3, 1, 257, 511, 257, 1, 3), # T + (1, 255, 257, 256, 257, 255, 1), # U + (1, 15, 113, 384, 113, 15, 1), # V + (1, 63, 449, 56, 7, 56, 449, 63, 1), # W + (257, 387, 365, 16, 365, 387, 257), # X + (1, 7, 281, 480, 281, 7, 1), # Y + (259, 385, 353, 273, 269, 387), # Z + (1023, 513, 513, 0), # [ + (6, 24, 96, 384, 0), # \ + (513, 513, 1023, 0), # ] + (4, 2, 1, 2, 4, 0), # ^ + (256, 256, 256, 256, 256, 256, 256), # _ + (1, 2, 4, 0), # ` + (200, 296, 296, 296, 240, 256), # a + (1, 511, 144, 264, 264, 240), # b + (240, 264, 264, 264, 144), # c + (240, 264, 264, 145, 511, 256), # d + (240, 296, 296, 296, 176), # e + (264, 510, 265, 265, 2), # f + (336, 680, 680, 680, 656, 264), # g + (257, 511, 272, 8, 264, 496, 256), # h + (264, 507, 256), # i + (128, 256, 264, 251, 0), # j + (257, 511, 320, 40, 344, 392, 256), # k + (257, 511, 256), # l + (264, 504, 272, 8, 496, 16, 264, 496, 256), # m + (264, 504, 272, 8, 264, 496, 256), # n + (240, 264, 264, 264, 240), # o + (520, 1016, 592, 136, 136, 112), # p + (112, 136, 136, 592, 1016, 520), # q + (264, 504, 272, 8, 16), # r + (144, 296, 296, 328, 144), # s + (8, 8, 254, 264, 264, 128), # t + (8, 248, 256, 256, 136, 504, 256), # u + (8, 56, 200, 256, 200, 56, 8), # v + (8, 120, 392, 96, 24, 96, 392, 120, 8), # w + (264, 144, 96, 144, 264), # x + (264, 536, 616, 384, 104, 24, 8), # y + (280, 392, 328, 296, 408), # z + (48, 462, 513, 0), # { + (0, 1023, 0), # | + (513, 462, 48, 0), # } + (16, 8, 8, 16, 32, 32, 16), # ~ +) \ No newline at end of file diff --git a/synchrod/synchrod.py b/synchrod/synchrod.py new file mode 100644 index 0000000..ce7d135 --- /dev/null +++ b/synchrod/synchrod.py @@ -0,0 +1,295 @@ +from polycal4 import get_infos, KEY_NONE, KEY_LEFT, KEY_UP, KEY_DOWN, KEY_RIGHT, KEY_ENTER, KEY_ESC + +screen_w, screen_h, poly_has_color, poly_set_pixel, draw_line, fill_rect, show_screen, init_clean_screen, wait_key, wait_release, esc_key, HAS_KEYS, poly_pause, sleep = get_infos(["w","h","hc","sp","dl","fr","sh","cli","wk","wr","ek","hk","p","s"], 256, 128, 4) +get_infos = None + +SCALE = min(screen_w // 128, screen_h // 64) + +YELLOW, RED, BLUE, GREEN, ALL = 0, 1, 2, 3, 4 +VOID, WALL, SPIKES, MONSTER, TRAP, EXIT = 100, 101, 102, 107, 112, 117 +GO_UP, GO_RIGHT, GO_DOWN, GO_LEFT, ATTACK, QUIT = 10, 11, 12, 13, 14, 15 +NEW_GAME, TRAP_APPEARED, SPIKES_APPEARED = 20, 21, 22 + +JAUNE, ROUGE, BLEU, VERT, TOUS = 0, 1, 2, 3, 4 +VIDE, MUR, PICS, MONSTRE, PIEGE, SORTIE = 100, 101, 102, 107, 112, 117 +ALLER_HAUT, ALLER_DROITE, ALLER_BAS, ALLER_GAUCHE, ATTAQUER, QUITTER = 10, 11, 12, 13, 14, 15 +NOUVELLE_PARTIE, PIEGE_APPARU, PICS_APPARUS = 20, 21, 22 + +def ri(rs,a,b): + rs[0] = (rs[0] * 214013 + 2531011) % 4294967296 + r = (rs[0] // 65536) & 0x7fff + return r % (b-a) + a + +### Rendering + +def px(x, y, c): + for sx in range(SCALE): + for sy in range(SCALE): + poly_set_pixel(x*SCALE+sx, y*SCALE+sy, c) + +if poly_has_color: + colors = [(77,71,63), (210,217,217), (241,233,103), (234,96,88), (97,156,236), (111,226,126), (178,126,206)] + s=0x002a2a55005454aa00 + m=0x00005ae7bd18000000 + t=0x00182c4a5234180000 + e=0xf080e0c080e0c0f000 + f=0x0f0107030107030f00 + p=0x386565396dd7ff7d00 + tiles = [0,0xff818181818181ff01,s+2,s+3,s+4,s+5,s+6,m+2,m+3,m+4,m+5,m+6,t+2,t+3,t+4,t+5,t+6,f+2,e+3,f+4,e+5] + sprites = [p+2,p+3,p+4,p+5] + + def render_image(tile, x0, y0): + fg, bg = colors[tile & 0xff], colors[0] + tile >>= 8 + if tile: + for y in range(8): + for x in range(8): + px(x0*8+7-x, y0*8+7-y, fg if tile & 1 else bg) + tile >>= 1 + else: + fill_rect(x0*8*SCALE, y0*8*SCALE, 8*SCALE, 8*SCALE, colors[0]) +else: + colors = [(255,255,255),(0,0,0)] + p1=0x040a00<<40 + p2=0x040a06<<40 + p3=0x040a0e<<40 + p4=0x040e04<<40 + s=0x5454aa00 + m=0x00005ae7bd18 + t=0x1028542810 + e=0xe080e0c080e0c0f0 + tiles = [0,0xff818181818181ff,s+p1,s+p2,s+p3,s+p4,(s<<31)+s,m+p1,m+p2,m+p3,m+p4,m<<16,t+p1,t+p2,t+p3,t+p4,t<<8,0x275107030107030f,e+p2,0x275177030107030f,e+p4] + sprites = [0x386565396dd7ff7d,0x386565396dd7e77d,0x386565396dd7c77d,0x386565396dc7ef7d] + + def render_image(tile, x0, y0): + if tile: + for y in range(8): + for x in range(8): + px(x0*8+7-x, y0*8+7-y, colors[tile & 1]) + tile >>= 1 + else: + fill_rect(x0*8*SCALE, y0*8*SCALE, 8*SCALE, 8*SCALE, colors[0]) + +def render_game(board, players, old_players, update): + for xy in update: + if xy == -1: + for y in range(8): + for x in range(16): + render_image(tiles[board[16*y+x]-VOID], x, y) + else: + render_image(tiles[board[xy]-VOID], xy%16, xy//16) + for xy in old_players: + render_image(tiles[board[xy]-VOID], xy%16, xy//16) + for i in range(4): + render_image(sprites[i], players[i]%16, players[i]//16) + +### API for submissions + +if HAS_KEYS: + interactive_actions = {KEY_UP:GO_UP, KEY_RIGHT:GO_RIGHT, KEY_DOWN:GO_DOWN, KEY_LEFT:GO_LEFT, KEY_ENTER:ATTACK, KEY_ESC:QUIT} +else: + interactive_actions = {"8":GO_UP, "6":GO_RIGHT, "2":GO_DOWN, "4":GO_LEFT, "5":ATTACK, "9":QUIT} + +def input_action(): + if HAS_KEYS: + wait_release() + i = KEY_NONE + while not i in interactive_actions.keys(): + i = wait_key() + else: + print("Déplacement:") + print(" 8") + print(" 4 6") + print(" 2") + print("Attaquer: 5") + print("Voir plateau: 0") + print("Quitter: 9") + i = input("Que faire ?> ") + return interactive_actions.get(i) + +def is_a(obj, kind): + if kind == VOID or kind == WALL: + return obj == kind + return kind <= obj <= kind + 4 + +def affects(obj, player): + return (is_a(obj, SPIKES) or is_a(obj, MONSTER) or is_a(obj, TRAP)) and obj != SPIKES+player and obj != MONSTER+player and obj != TRAP+player + +pathfind_d = [-16, +16, -1, +1, GO_UP, GO_DOWN, GO_LEFT, GO_RIGHT] + +def pathfind(board, start, end): + n = len(board) + parent = [-1]*n + directions = [-1]*n + queue = [start] + parent[start] = start + cell = -1 + + while queue: + cell = queue.pop(0) + if cell == end: + break + for i in range(4): + d = cell+pathfind_d[i] + if board[d] != WALL and parent[d] < 0: + parent[d] = cell + directions[d] = pathfind_d[i+4] + queue.append(d) + + if cell == end: + path = [] + while cell != start: + path = [directions[cell]] + path + cell = parent[cell] + return path + +demander_action, est_un, affecte, calculer_chemin = input_action, is_a, affects, pathfind + +### Board generator + +def ufc(): + return [i for i in range(128)] +def ufr(uf,i): + if uf[i] == i: + return i + rep = ufr(uf, uf[i]) + uf[i] = rep + return rep +def ufm(uf,i,j): + uf[ufr(uf,i)]=ufr(uf,j) + +def gb(rs): + board = [WALL] * 128 + pp = [17,30,97,110] + pe = [47,80,95,32] + for i in range(4): + board[pp[i]] = VOID + board[pe[i]] = EXIT+i + uf = [i for i in range(128)] + while any(ufr(uf,pp[i]) != ufr(uf,pe[i]) for i in range(4)): + i = ri(rs,1,7)*16 + ri(rs,1,15) + if board[i] == WALL and any(board[n] != VOID for n in (i-16,i+16,i-1,i+1)): + board[i] = VOID + for n in (i-16,i+16,i-1,i+1): + if board[n] != WALL: + ufm(uf,i,n) + for i in range(128): + if board[i] == VOID and ri(rs,0,16)<2: + board[i]=[MONSTER, MONSTER, SPIKES, TRAP][ri(rs,0,4)]+ri(rs,0,5) + return board + +### Game logic + +def play_board(rs, turn_function, blind): + board = gb(rs) + players = [17,30,97,110] + old_players = [] + ev = [(-1,-1,NEW_GAME,-1)] + update = [-1] + turns = 0 + penalty = 0 + + while True: + if not blind: + render_game(board, players, old_players, update) + show_screen() + if not HAS_KEYS: + poly_pause() + action = turn_function(board, players, ev) + ev = [] + update = [] + old_players = players[:] + if action is None: + continue + if action == QUIT: + return None + + turns += 1 + traps_activated = 0 + + for p in range(4): + coord = players[p] + if coord < 0: + continue + + if action == ATTACK: + for direction in range(4): + dx = (direction == 1) - (direction == 3) + dy = (direction == 2) - (direction == 0) + i = coord + dx + 16*dy + dest = board[i] + + if is_a(dest, MONSTER): + board[i] = VOID + update.append(i) + elif is_a(dest, TRAP) and affects(dest, p): + traps_activated += 1 + board[i] = VOID + update.append(i) + else: + dx = (action == GO_RIGHT) - (action == GO_LEFT) + dy = (action == GO_DOWN) - (action == GO_UP) + i = coord + dx + 16*dy + dest = board[i] + + if dest == EXIT+p: + players[p] = -1 + continue + if dest == WALL or is_a(dest, EXIT): + continue + + players[p] = i + if not affects(dest, p): + continue + + if is_a(dest, SPIKES): + penalty += 10 + elif is_a(dest, MONSTER): + penalty += 10 + board[i] = VOID + update.append(i) + elif is_a(dest, TRAP): + traps_activated += 1 + board[i] = VOID + update.append(i) + + while traps_activated > 0: + effect = ri(rs,0,3) + if effect == 0: + penalty += 10 + x = ri(rs,1,15) + y = ri(rs,1,7) + if board[y*16+x] == VOID and y*16+x not in players: + board[y*16+x] = (TRAP if effect==1 else SPIKES) + ri(rs,0,4) + ev.append((x, y, TRAP_APPEARED if effect==1 else SPIKES_APPEARED, p)) + update.append(y*16+x) + traps_activated -= 1 + + if all(p < 0 for p in players): + score = 150 - penalty - turns + print("Bravo! "+str(turns)+"T "+str(penalty)+"D -> "+str(score)) + return score + +def play_game(turn_function, blind=False, seed=0xc0ffee, maxgames=100): + if not blind: + init_clean_screen() + rs = [seed] + games = 0 + score = 0 + while games != maxgames: + print("#"+str(games)+": "+str(rs[0])) + board_score = play_board(rs, turn_function, blind) + if board_score is None: + print("") + print("Quit!") + return + else: + score += max(board_score, 0) + games += 1 + print("Games solved:", games) + print("Score:", score) + +if __name__ == "__main__": + def turn(board, player, ev): + return input_action() + play_game(turn)