143 lines
4.1 KiB
Python
143 lines
4.1 KiB
Python
import enum
|
|
import pyglet
|
|
import random
|
|
import rectangulararray
|
|
|
|
field_width = 60
|
|
field_height = 20
|
|
max_level = 2
|
|
field = rectangulararray.RectangularArray((field_width, field_height), default = int(max_level / 2))
|
|
|
|
do_update = True
|
|
run_simulation = True
|
|
|
|
def update(dt):
|
|
if not do_update or not run_simulation:
|
|
return
|
|
|
|
deltas = rectangulararray.RectangularArray((field_width, field_height), default = 0)
|
|
|
|
# Calculate deltas
|
|
for y in range(field_height):
|
|
for x in range(field_width):
|
|
# First drop down
|
|
if (y + 1) in range(field_height) and field[x, y + 1] < max_level:
|
|
change = min(field[x, y] + deltas [x, y], max_level - field[x, y + 1] - deltas[x, y + 1])
|
|
deltas[x, y] -= change
|
|
deltas[x, y + 1] += change
|
|
|
|
# Then see if any side-neighbour is lower than us
|
|
left_lower = (x - 1) in range(field_width) and field[x - 1, y] < field[x, y]
|
|
right_lower = (x + 1) in range(field_width) and field[x + 1, y] < field[x, y]
|
|
|
|
# Move to lower side, or if both have same level, randomly
|
|
move_left = False
|
|
move_right = False
|
|
if left_lower and not right_lower:
|
|
move_left = True
|
|
elif not left_lower and right_lower:
|
|
move_right = True
|
|
elif left_lower and right_lower:
|
|
left_value = field[x - 1, y]
|
|
right_value = field[x + 1, y]
|
|
move_left = left_value < right_value
|
|
move_right = left_value > right_value
|
|
|
|
if not move_left and not move_right:
|
|
move_left = random.randint(0, 1) == 0
|
|
move_right = not move_left
|
|
|
|
assert(not (move_left and move_right))
|
|
|
|
if move_left:
|
|
change = min(int((field[x, y] + deltas [x, y]) / 2), max_level - field[x - 1, y] - deltas[x - 1, y])
|
|
deltas[x, y] -= change
|
|
deltas[x - 1, y] += change
|
|
elif move_right:
|
|
change = min(int((field[x, y] + deltas [x, y]) / 2), max_level - field[x + 1, y] - deltas[x + 1, y])
|
|
deltas[x, y] -= change
|
|
deltas[x + 1, y] += change
|
|
|
|
# Apply deltas
|
|
for y in range(field_height):
|
|
for x in range(field_width):
|
|
field[x, y] += deltas[x, y]
|
|
|
|
window_width = field_width * (max_level * 7 + 1) + 1
|
|
window_height = field_height * (max_level * 7 + 1) + 1
|
|
window = pyglet.window.Window(window_width, window_height, resizable = True)
|
|
|
|
pyglet.clock.schedule_interval(update, 1/4)
|
|
|
|
def get_cell(x, y):
|
|
y = window_height - y - 1
|
|
cell_x = int((x - 1) * field_width / window_width)
|
|
cell_y = int((y - 1) * field_height / window_height)
|
|
return cell_x, cell_y
|
|
|
|
@window.event
|
|
def on_mouse_press(x, y, button, modifiers):
|
|
global do_update
|
|
|
|
assert(0 < x <= window_width)
|
|
assert(0 < y <= window_height)
|
|
|
|
if button == pyglet.window.mouse.LEFT:
|
|
cell_x, cell_y = get_cell(x, y)
|
|
field[cell_x, cell_y] = max_level
|
|
do_update = False
|
|
|
|
elif button == pyglet.window.mouse.RIGHT:
|
|
cell_x, cell_y = get_cell(x, y)
|
|
field[cell_x, cell_y] = 0
|
|
do_update = False
|
|
|
|
@window.event
|
|
def on_key_press(symbol, modifiers):
|
|
global run_simulation
|
|
if symbol == pyglet.window.key.SPACE:
|
|
run_simulation = not run_simulation
|
|
|
|
def scale_h(x):
|
|
return int(x * (window_width - 1) / field_width)
|
|
|
|
def scale_v(y):
|
|
return int(y * (window_height - 1) / field_height)
|
|
|
|
def draw_cell(coordinates):
|
|
x1, y1, x2, y2 = coordinates
|
|
color = (127, 127, 255)
|
|
# Tranform coordinates from the internal top-left origin to OpenGL's bottom-left origin
|
|
y1 = window_height - y1 - 1
|
|
y2 = window_height - y2 - 1
|
|
pyglet.graphics.draw(4, pyglet.gl.GL_QUADS, ('v2i', (x1, y1, x2, y1, x2, y2, x1, y2)), ('c3B', 4*color))
|
|
|
|
def get_coordinates(x, y, level):
|
|
assert(0 <= x <= field_width)
|
|
assert(0 <= y <= field_height)
|
|
assert(0 <= level <= max_level)
|
|
screen_x1 = scale_h(x) + 1
|
|
screen_y1 = scale_v(y) + 1
|
|
screen_y1 += int((max_level - level) / max_level * ((window_height - 1) / field_height - 1))
|
|
screen_x2 = scale_h(x + 1)
|
|
screen_y2 = scale_v(y + 1)
|
|
return (screen_x1, screen_y1, screen_x2, screen_y2)
|
|
|
|
@window.event
|
|
def on_draw():
|
|
global do_update
|
|
window.clear()
|
|
for y in range(field_height):
|
|
for x in range(field_width):
|
|
coordinates = get_coordinates(x, y, field[x, y])
|
|
draw_cell(coordinates)
|
|
|
|
do_update = True
|
|
|
|
@window.event
|
|
def on_resize(width, height):
|
|
global window_width, window_height
|
|
window_width, window_height = width, height
|
|
|
|
pyglet.app.run()
|