Let's throw this onto git
This commit is contained in:
commit
e330b18748
|
@ -0,0 +1,3 @@
|
|||
__pycache__
|
||||
*.pyc
|
||||
*.swp
|
|
@ -0,0 +1,116 @@
|
|||
CC0 1.0 Universal
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator and
|
||||
subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for the
|
||||
purpose of contributing to a commons of creative, cultural and scientific
|
||||
works ("Commons") that the public can reliably and without fear of later
|
||||
claims of infringement build upon, modify, incorporate in other works, reuse
|
||||
and redistribute as freely as possible in any form whatsoever and for any
|
||||
purposes, including without limitation commercial purposes. These owners may
|
||||
contribute to the Commons to promote the ideal of a free culture and the
|
||||
further production of creative, cultural and scientific works, or to gain
|
||||
reputation or greater distribution for their Work in part through the use and
|
||||
efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any expectation
|
||||
of additional consideration or compensation, the person associating CC0 with a
|
||||
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
|
||||
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
|
||||
and publicly distribute the Work under its terms, with knowledge of his or her
|
||||
Copyright and Related Rights in the Work and the meaning and intended legal
|
||||
effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not limited
|
||||
to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display, communicate,
|
||||
and translate a Work;
|
||||
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
|
||||
iii. publicity and privacy rights pertaining to a person's image or likeness
|
||||
depicted in a Work;
|
||||
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data in
|
||||
a Work;
|
||||
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation thereof,
|
||||
including any amended or successor version of such directive); and
|
||||
|
||||
vii. other similar, equivalent or corresponding rights throughout the world
|
||||
based on applicable law or treaty, and any national implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention of,
|
||||
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
|
||||
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
|
||||
and Related Rights and associated claims and causes of action, whether now
|
||||
known or unknown (including existing as well as future claims and causes of
|
||||
action), in the Work (i) in all territories worldwide, (ii) for the maximum
|
||||
duration provided by applicable law or treaty (including future time
|
||||
extensions), (iii) in any current or future medium and for any number of
|
||||
copies, and (iv) for any purpose whatsoever, including without limitation
|
||||
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
|
||||
the Waiver for the benefit of each member of the public at large and to the
|
||||
detriment of Affirmer's heirs and successors, fully intending that such Waiver
|
||||
shall not be subject to revocation, rescission, cancellation, termination, or
|
||||
any other legal or equitable action to disrupt the quiet enjoyment of the Work
|
||||
by the public as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason be
|
||||
judged legally invalid or ineffective under applicable law, then the Waiver
|
||||
shall be preserved to the maximum extent permitted taking into account
|
||||
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
|
||||
is so judged Affirmer hereby grants to each affected person a royalty-free,
|
||||
non transferable, non sublicensable, non exclusive, irrevocable and
|
||||
unconditional license to exercise Affirmer's Copyright and Related Rights in
|
||||
the Work (i) in all territories worldwide, (ii) for the maximum duration
|
||||
provided by applicable law or treaty (including future time extensions), (iii)
|
||||
in any current or future medium and for any number of copies, and (iv) for any
|
||||
purpose whatsoever, including without limitation commercial, advertising or
|
||||
promotional purposes (the "License"). The License shall be deemed effective as
|
||||
of the date CC0 was applied by Affirmer to the Work. Should any part of the
|
||||
License for any reason be judged legally invalid or ineffective under
|
||||
applicable law, such partial invalidity or ineffectiveness shall not
|
||||
invalidate the remainder of the License, and in such case Affirmer hereby
|
||||
affirms that he or she will not (i) exercise any of his or her remaining
|
||||
Copyright and Related Rights in the Work or (ii) assert any associated claims
|
||||
and causes of action with respect to the Work, in either case contrary to
|
||||
Affirmer's express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
|
||||
b. Affirmer offers the Work as-is and makes no representations or warranties
|
||||
of any kind concerning the Work, express, implied, statutory or otherwise,
|
||||
including without limitation warranties of title, merchantability, fitness
|
||||
for a particular purpose, non infringement, or the absence of latent or
|
||||
other defects, accuracy, or the present or absence of errors, whether or not
|
||||
discoverable, all to the greatest extent permissible under applicable law.
|
||||
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without limitation
|
||||
any person's Copyright and Related Rights in the Work. Further, Affirmer
|
||||
disclaims responsibility for obtaining any necessary consents, permissions
|
||||
or other rights required for any use of the Work.
|
||||
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to this
|
||||
CC0 or use of the Work.
|
||||
|
||||
For more information, please see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>
|
|
@ -0,0 +1,59 @@
|
|||
# v1
|
||||
# Written by nortti
|
||||
# Under Unlicense
|
||||
|
||||
from functools import reduce
|
||||
import operator
|
||||
|
||||
class RankError(Exception):
|
||||
None
|
||||
|
||||
class RectangularArray:
|
||||
def __init__(self, sizes, *, default = None, initial = None):
|
||||
if isinstance(sizes, int):
|
||||
self.sizes = (sizes,)
|
||||
else:
|
||||
self.sizes = tuple(sizes)
|
||||
|
||||
self.rank = len(self.sizes)
|
||||
|
||||
combined_size = reduce(operator.mul, self.sizes)
|
||||
|
||||
if initial is None:
|
||||
self.array = [default] * combined_size
|
||||
else:
|
||||
if len(initial) != combined_size:
|
||||
raise ValueError('Initial array of wrong size passed (expected %i, got %i)' % (combined_size, len(initial)))
|
||||
|
||||
|
||||
def __get_location_from_indices(self, indices):
|
||||
if isinstance(indices, int):
|
||||
indices = (indices,)
|
||||
else:
|
||||
indices = tuple(indices)
|
||||
|
||||
if len(indices) != self.rank:
|
||||
raise RankError('Rank mismatch accessing array (expected %i, got %i)' % (self.rank, len(indices)))
|
||||
|
||||
location = 0
|
||||
for dimension in range(self.rank):
|
||||
index = indices[dimension]
|
||||
size = self.sizes[dimension]
|
||||
if index >= size:
|
||||
raise IndexError('Index Out of Range (is %i, max %i)' % (index, self.sizes[dimension] - 1))
|
||||
|
||||
location *= size
|
||||
location += index
|
||||
|
||||
return location
|
||||
|
||||
def __getitem__(self, indices):
|
||||
location = self.__get_location_from_indices(indices)
|
||||
return self.array[location]
|
||||
|
||||
def __setitem__(self, indices, value):
|
||||
location = self.__get_location_from_indices(indices)
|
||||
self.array[location] = value
|
||||
|
||||
def __repr__(self):
|
||||
return 'RectangularArray(%s, initial = %s)' % (repr(self.sizes), repr(self.array))
|
|
@ -0,0 +1,142 @@
|
|||
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()
|
Loading…
Reference in New Issue