mirror of
https://github.com/openbsd/ports.git
synced 2026-06-17 23:13:55 +02:00
drop mysticmine BDEP on python2 cython
Convert ai.pyx to ai.py which allows us to drop the build dependency on python2 cython. This will allow us to make cython python3-only.
This commit is contained in:
@@ -6,7 +6,7 @@ DISTNAME = mysticmine-${MODPY_EGG_VERSION}
|
||||
GH_ACCOUNT = dewitters
|
||||
GH_PROJECT = MysticMine
|
||||
GH_COMMIT = 2fc0a5eaa0ab299c3a23ce17ae1c56a98055a44c
|
||||
REVISION = 3
|
||||
REVISION = 4
|
||||
|
||||
CATEGORIES = games
|
||||
|
||||
@@ -17,7 +17,7 @@ WANTLIB = pthread ${MODPY_WANTLIB}
|
||||
|
||||
MODULES = lang/python
|
||||
MODPY_VERSION = ${MODPY_DEFAULT_VERSION_2}
|
||||
BUILD_DEPENDS = lang/cython${MODPY_FLAVOR}
|
||||
|
||||
RUN_DEPENDS = devel/pygame${MODPY_FLAVOR} \
|
||||
math/py2-numpy
|
||||
|
||||
|
||||
@@ -0,0 +1,467 @@
|
||||
convert ai.pyx -> ai.py to remove cython2 dependency
|
||||
|
||||
Index: monorail/ai.py
|
||||
--- monorail/ai.py.orig
|
||||
+++ monorail/ai.py
|
||||
@@ -0,0 +1,461 @@
|
||||
+import copy
|
||||
+import tiles
|
||||
+import pickups
|
||||
+
|
||||
+
|
||||
+class AiNode:
|
||||
+
|
||||
+ def __init__(self, parent):
|
||||
+ """Creates a new instance when a parent is known.
|
||||
+
|
||||
+ When parent is unknown, use static method 'create'.
|
||||
+ """
|
||||
+ self.parent = parent
|
||||
+
|
||||
+ def _generate_childeren( self ):
|
||||
+ """Generate and return the list of childeren nodes of this node
|
||||
+ """
|
||||
+ childeren = []
|
||||
+ trailnodes = self.trailnode.get_out_nodes()
|
||||
+ for n in trailnodes:
|
||||
+ node = AiNode( self )
|
||||
+
|
||||
+ node.carstate = self.carstate
|
||||
+ node.playfieldstate = self.playfieldstate
|
||||
+ node.other_trees = self.other_trees
|
||||
+
|
||||
+ node.trailnode = n
|
||||
+ childeren.append( node )
|
||||
+
|
||||
+ return childeren
|
||||
+
|
||||
+ def set_playfield( self, playfield ):
|
||||
+ self.playfieldstate = PlayfieldState( playfield )
|
||||
+
|
||||
+ def set_other_trees( self, other_trees ):
|
||||
+ self.other_trees = other_trees
|
||||
+
|
||||
+ def _calc_score( self, distance ):
|
||||
+ if self.parent is not None:
|
||||
+ self.playfieldstate = self.parent.playfieldstate
|
||||
+ self.carstate = self.parent.carstate
|
||||
+
|
||||
+ node = self
|
||||
+
|
||||
+ if node.playfieldstate is None:
|
||||
+ print("playfieldstate shouldn't be None in real game")
|
||||
+ return 0
|
||||
+
|
||||
+ score = 0
|
||||
+
|
||||
+ if isinstance(node.trailnode.tile, tiles.Enterance):
|
||||
+ if isinstance( node.carstate.collectible, pickups.Diamond ):
|
||||
+ node.carstate = copy.copy( node.carstate )
|
||||
+ node.carstate.collectible = None
|
||||
+ score = score + 2
|
||||
+
|
||||
+ score = score + self._calc_tile_pickups( node )
|
||||
+ score = score + self._calc_other_cars( node, distance )
|
||||
+
|
||||
+ return score
|
||||
+
|
||||
+ def _calc_tile_pickups( self, node ):
|
||||
+ score = 0
|
||||
+
|
||||
+ if node.playfieldstate.get_pickup( node.trailnode.tile ) is not None:
|
||||
+ if isinstance(node.trailnode.tile.pickup, pickups.CopperCoin):
|
||||
+ score = 1
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.GoldBlock):
|
||||
+ if isinstance( node.carstate.collectible, pickups.Axe ):
|
||||
+ score = 1
|
||||
+ else:
|
||||
+ score = 0.1
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.RockBlock):
|
||||
+ score = -1
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.Diamond):
|
||||
+ if not isinstance(node.carstate.collectible, pickups.Diamond):
|
||||
+ score = 1
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.Dynamite):
|
||||
+ score = -8
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.Lamp):
|
||||
+ score = 5
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.Axe):
|
||||
+ score = 2
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.Flag):
|
||||
+ if self.carstate.goldcar.nr == node.trailnode.tile.pickup.goldcar.nr:
|
||||
+ score = 1
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.Leprechaun):
|
||||
+ score = -2
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.Torch):
|
||||
+ score = 0
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.Key):
|
||||
+ score = 1
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.Mirror):
|
||||
+ score = 1
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.Oiler):
|
||||
+ score = 1
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.Multiplier):
|
||||
+ score = 1
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.Balloon):
|
||||
+ score = 0
|
||||
+ elif isinstance(node.trailnode.tile.pickup, pickups.Ghost):
|
||||
+ score = 1
|
||||
+
|
||||
+ if isinstance( node.trailnode.tile.pickup, pickups.Collectible ):
|
||||
+ node.carstate = copy.copy( node.carstate )
|
||||
+ node.carstate.collectible = node.trailnode.tile.pickup
|
||||
+ elif isinstance( node.trailnode.tile.pickup, pickups.PowerUp ):
|
||||
+ node.carstate = copy.copy( node.carstate )
|
||||
+ node.carstate.modifier = node.trailnode.tile.pickup
|
||||
+
|
||||
+ node.playfieldstate = node.playfieldstate.clone()
|
||||
+ node.playfieldstate.remove_pickup( node.trailnode.tile )
|
||||
+
|
||||
+ return score
|
||||
+
|
||||
+ def _calc_other_cars( self, node, distance ):
|
||||
+ score = 0
|
||||
+
|
||||
+ for tree in self.other_trees:
|
||||
+ gen_nodes = tree.get_nodes_of_generation( distance )
|
||||
+ for ai_node in gen_nodes:
|
||||
+ othernode = ai_node.smartnode
|
||||
+ if node.trailnode.tile is othernode.trailnode.tile:
|
||||
+ if othernode.carstate.goldcar.collectible is not None:
|
||||
+ if othernode.carstate.goldcar.collectible.is_good():
|
||||
+ score = score + 5.0 / len( gen_nodes ) / max(distance, 1)
|
||||
+ elif othernode.carstate.goldcar.collectible.is_bad():
|
||||
+ score = score - 5.0 / len( gen_nodes ) / max(distance, 1)
|
||||
+ if node.carstate.goldcar.collectible is not None:
|
||||
+ if node.carstate.goldcar.collectible.is_good():
|
||||
+ score = score - 5.0 / len( gen_nodes ) / max(distance, 1)
|
||||
+ elif node.carstate.goldcar.collectible.is_bad():
|
||||
+ score = score + 5.0 / len( gen_nodes ) / max(distance, 1)
|
||||
+
|
||||
+ # Calc parent node when crossing (can pass by)
|
||||
+ if node.parent is not None and\
|
||||
+ node.parent.trailnode.tile is othernode.trailnode.tile and\
|
||||
+ node.parent.trailnode.in_dir != othernode.trailnode.in_dir:
|
||||
+ if othernode.carstate.goldcar.collectible is not None:
|
||||
+ if othernode.carstate.goldcar.collectible.is_good():
|
||||
+ score = score + 5.0 / len( gen_nodes ) / max(distance, 1)
|
||||
+ elif othernode.carstate.goldcar.collectible.is_bad():
|
||||
+ score = score - 5.0 / len( gen_nodes ) / max(distance, 1)
|
||||
+ if node.parent.carstate.goldcar.collectible is not None:
|
||||
+ if node.parent.carstate.goldcar.collectible.is_good():
|
||||
+ score = score - 5.0 / len( gen_nodes ) / max(distance, 1)
|
||||
+ elif node.parent.carstate.goldcar.collectible.is_bad():
|
||||
+ score = score + 5.0 / len( gen_nodes ) / max(distance, 1)
|
||||
+
|
||||
+ return score
|
||||
+
|
||||
+ def equals( self, other ):
|
||||
+ return self.trailnode.tile is other.trailnode.tile and \
|
||||
+ self.trailnode.in_dir.__eq__(other.trailnode.in_dir)
|
||||
+
|
||||
+ def nequals( self, other ):
|
||||
+ return not self.equals(other)
|
||||
+
|
||||
+
|
||||
+class Node:
|
||||
+ """An abstract node used in PredictionTree.
|
||||
+
|
||||
+ public members:
|
||||
+ - generation: the generation of this node
|
||||
+ - parent: this nodes parent
|
||||
+ """
|
||||
+
|
||||
+ def __init__( self, smartnode, parent=None ):
|
||||
+ self.smartnode = smartnode
|
||||
+ self.parent = parent
|
||||
+ self.childeren = None
|
||||
+ self._best_score = -999
|
||||
+
|
||||
+ if parent is not None:
|
||||
+ self.generation = parent.generation+1
|
||||
+ else:
|
||||
+ self.generation = 0
|
||||
+
|
||||
+ def generate_childeren( self ):
|
||||
+ """Generate and return the list of childeren nodes of this node
|
||||
+ """
|
||||
+ childeren = self.smartnode._generate_childeren()
|
||||
+
|
||||
+ self.childeren = []
|
||||
+ for child in childeren:
|
||||
+ self.childeren.append( Node(child, self) )
|
||||
+
|
||||
+ return self.childeren
|
||||
+
|
||||
+ def get_generation( self, ancestor_node ):
|
||||
+ generation = 0
|
||||
+ node_it = self
|
||||
+ while node_it is not None and node_it is not ancestor_node:
|
||||
+ node_it = node_it.parent
|
||||
+ generation = generation + 1
|
||||
+
|
||||
+ if node_it is None:
|
||||
+ generation = -1
|
||||
+
|
||||
+ return generation
|
||||
+
|
||||
+ # used externally!!!!
|
||||
+ def get_childeren( self ):
|
||||
+ return self.childeren
|
||||
+
|
||||
+ def calc_score( self ):
|
||||
+ """Return the individual score of this node
|
||||
+ """
|
||||
+ return self.smartnode._calc_score(self.generation)
|
||||
+
|
||||
+ def get_total_score( self ):
|
||||
+ node = self
|
||||
+ total_score = 0
|
||||
+ i = 0
|
||||
+ while node is not None:
|
||||
+ i = i + 1
|
||||
+
|
||||
+ total_score = total_score + node.score * i
|
||||
+ node = node.parent
|
||||
+
|
||||
+ return total_score / i
|
||||
+
|
||||
+ def set_score( self, score ):
|
||||
+ """Set the score of this node, and possibly update other nodes in its path.
|
||||
+
|
||||
+ Other nodes are only updated if this node didn't have best score, or
|
||||
+ when it's a leaf node.
|
||||
+ """
|
||||
+ self.score = score
|
||||
+
|
||||
+ # handle best score
|
||||
+ if self.is_leaf() or self._best_score == -999:
|
||||
+ self._best_score = self.get_total_score()
|
||||
+ if self.parent is not None:
|
||||
+ self.parent._recalc_best_score()
|
||||
+
|
||||
+ def _recalc_best_score( self ):
|
||||
+ old_score = self._best_score
|
||||
+
|
||||
+ self._best_score = -999
|
||||
+ for child in self.childeren:
|
||||
+ if self._best_score == -999 or \
|
||||
+ child._best_score > self._best_score:
|
||||
+ self._best_score = child._best_score
|
||||
+
|
||||
+ if self._best_score != old_score and \
|
||||
+ self.parent is not None:
|
||||
+ self.parent._recalc_best_score()
|
||||
+
|
||||
+ # used externally!!!
|
||||
+ def get_best_score( self ):
|
||||
+ """Return the highest score that this node can reach.
|
||||
+ """
|
||||
+ return self._best_score
|
||||
+
|
||||
+ # used externally!!!
|
||||
+ def get_score( self ):
|
||||
+ return self.score
|
||||
+
|
||||
+ def get_best_childs( self ):
|
||||
+
|
||||
+ if self.childeren is None:
|
||||
+ return []
|
||||
+
|
||||
+ best_childs = []
|
||||
+ for child in self.childeren:
|
||||
+ best_child = None
|
||||
+ if len(best_childs) > 0:
|
||||
+ best_child = best_childs[0]
|
||||
+
|
||||
+ if (len(best_childs) == 0 or child._best_score > best_child._best_score):
|
||||
+ best_childs = [child]
|
||||
+ elif child._best_score == best_child._best_score:
|
||||
+ best_childs.append( child )
|
||||
+
|
||||
+ return best_childs
|
||||
+
|
||||
+ def is_leaf( self ):
|
||||
+ return (self.childeren is None) or (len( self.childeren ) == 0)
|
||||
+
|
||||
+ CYCLES_PER_UPDATE = 256*4
|
||||
+
|
||||
+
|
||||
+class PredictionTree:
|
||||
+ """A tree structure that contains all possible future moves with scores.
|
||||
+
|
||||
+ Public members:
|
||||
+ - root_node: the root node of this tree
|
||||
+ - total_generations: the total generations of this tree
|
||||
+ """
|
||||
+
|
||||
+ def __init__( self, MAX_NODES=256*2, CYCLES_PER_UPDATE=256 ):
|
||||
+ self.root_node = None
|
||||
+ self.total_generations = 0
|
||||
+ self.generations = []
|
||||
+ self.MAX_NODES = MAX_NODES
|
||||
+ self.nodes_calc = None
|
||||
+ self.CYCLES_PER_UPDATE = CYCLES_PER_UPDATE
|
||||
+ self.node_cnt = 0
|
||||
+
|
||||
+ def update( self ):
|
||||
+ """Update the tree as much as possible, using CYCLES_PER_UPDATE as upper limit.
|
||||
+ """
|
||||
+
|
||||
+ if self.root_node is not None:
|
||||
+ # We make sure we calculate all in limited time
|
||||
+ cycles_left = self.CYCLES_PER_UPDATE*2/3
|
||||
+ cycles_left = self._update_tree( cycles_left )
|
||||
+
|
||||
+ cycles_left = cycles_left + self.CYCLES_PER_UPDATE*1/3
|
||||
+ cycles_left = self._calc_nodes_scores( cycles_left )
|
||||
+
|
||||
+ self._update_tree( cycles_left )
|
||||
+
|
||||
+ def set_root( self, node ):
|
||||
+ """Change the root node in an optimized way.
|
||||
+
|
||||
+ If the node equals a child of the current root, then the current tree is
|
||||
+ reused.
|
||||
+ """
|
||||
+
|
||||
+ found = False
|
||||
+ # first try one of its childeren
|
||||
+ if self.root_node is not None:
|
||||
+ for child in self.root_node.childeren:
|
||||
+ if child.smartnode.equals(node.smartnode):
|
||||
+ found = True
|
||||
+ self.root_node = child
|
||||
+ self.root_node.parent = None
|
||||
+ self.total_generations = self.total_generations - 1
|
||||
+ self.nodes_calc = [self.root_node] # Recalculate scores of nodes
|
||||
+
|
||||
+ if not found:
|
||||
+ self.root_node = node
|
||||
+ self.leafs = [self.root_node]
|
||||
+ self.total_generations = 0
|
||||
+ self.nodes_calc = [self.root_node] # Recalculate scores of nodes
|
||||
+
|
||||
+ self._update_generations()
|
||||
+
|
||||
+ def _update_generations( self ):
|
||||
+ """Update our generation nodes
|
||||
+ """
|
||||
+ assert self.root_node is not None, "Don't call this when root_node is None"
|
||||
+
|
||||
+ self.generations = []
|
||||
+ self.node_cnt = 0
|
||||
+
|
||||
+ generation = [self.root_node]
|
||||
+ while len( generation ) > 0:
|
||||
+ next_generation = []
|
||||
+ for node in generation:
|
||||
+ self.node_cnt = self.node_cnt + 1
|
||||
+ node.generation = len( self.generations )
|
||||
+ childeren = node.childeren
|
||||
+ if childeren is not None:
|
||||
+ next_generation.extend( childeren )
|
||||
+
|
||||
+ self.generations.append( generation )
|
||||
+ generation = next_generation
|
||||
+
|
||||
+ def _update_tree( self, cycles_left ):
|
||||
+ """Update the tree until the maximum of generations is reached.
|
||||
+ """
|
||||
+
|
||||
+ assert self.root_node is not None, "Don't call this when root_node is None"
|
||||
+
|
||||
+ while self.node_cnt < self.MAX_NODES and len(self.leafs) > 0 and cycles_left > 0:
|
||||
+ cycles_left = cycles_left - 1
|
||||
+
|
||||
+ node = self.leafs.pop(0)
|
||||
+ gen = node.get_generation( self.root_node )
|
||||
+ if gen != -1: # else it's a leaf of old root_node
|
||||
+ self.total_generations = gen - 1
|
||||
+ nodes = node.generate_childeren()
|
||||
+ self.node_cnt = self.node_cnt + len(nodes)
|
||||
+ self.leafs.extend( nodes )
|
||||
+
|
||||
+ node.generation = gen
|
||||
+ self.get_nodes_of_generation( gen+1 ).extend( nodes )
|
||||
+
|
||||
+ return cycles_left
|
||||
+
|
||||
+ def _calc_nodes_scores( self, cycles_left ):
|
||||
+ """Calculate the score of the nodes that aren't calculated yet.
|
||||
+ """
|
||||
+ assert self.root_node is not None, "Don't call this when root_node is None"
|
||||
+
|
||||
+ if len( self.nodes_calc ) == 0:
|
||||
+ self.nodes_calc = [self.root_node]
|
||||
+
|
||||
+ while len(self.nodes_calc) > 0 and cycles_left > 0:
|
||||
+ cycles_left = cycles_left - 1
|
||||
+
|
||||
+ node = self.nodes_calc.pop(0)
|
||||
+ node.set_score( node.calc_score() )
|
||||
+
|
||||
+ childeren = node.childeren
|
||||
+ if childeren is not None:
|
||||
+ self.nodes_calc.extend( childeren )
|
||||
+
|
||||
+ return cycles_left
|
||||
+
|
||||
+ def get_nodes_of_generation( self, gen ):
|
||||
+ """Return all the nodes of the generation.
|
||||
+ """
|
||||
+ if gen is not None and gen > -1 and gen < len( self.generations ):
|
||||
+ return self.generations[ gen ]
|
||||
+ else:
|
||||
+ return []
|
||||
+
|
||||
+
|
||||
+class PlayfieldState:
|
||||
+
|
||||
+ def __init__( self, playfield ):
|
||||
+ self.playfield = playfield
|
||||
+
|
||||
+ self.pickups = []
|
||||
+ for tile in self.playfield.level.tiles:
|
||||
+ if tile.pickup is not None:
|
||||
+ self.pickups.append( [tile, tile.pickup] )
|
||||
+
|
||||
+ def reset( self ):
|
||||
+ self.pickups = []
|
||||
+ for tile in self.playfield.level.tiles:
|
||||
+ if tile.pickup is not None:
|
||||
+ self.pickups.append( [tile, tile.pickup] )
|
||||
+
|
||||
+ def get_pickup( self, tile ):
|
||||
+ for (t, pickup) in self.pickups:
|
||||
+ if tile is t:
|
||||
+ return pickup
|
||||
+
|
||||
+ return None
|
||||
+
|
||||
+ def remove_pickup( self, tile ):
|
||||
+ remove_index = None
|
||||
+ i = 0
|
||||
+ for (t, pickup) in self.pickups:
|
||||
+ if tile is t:
|
||||
+ remove_index = i
|
||||
+ i = i + 1
|
||||
+
|
||||
+ if remove_index is not None:
|
||||
+ del self.pickups[remove_index]
|
||||
+
|
||||
+ def clone( self ):
|
||||
+ clone = PlayfieldState( self.playfield )
|
||||
+ clone.pickups = copy.copy( self.pickups )
|
||||
+ return clone
|
||||
+
|
||||
+
|
||||
+def AiNode_create( goldcarstate, trailnode=None ):
|
||||
+ self = AiNode( None )
|
||||
+
|
||||
+ self.trailnode = trailnode
|
||||
+ self.carstate = goldcarstate
|
||||
+ self.playfieldstate = None
|
||||
+ self.other_trees = None
|
||||
+
|
||||
+ return self
|
||||
@@ -1,15 +1,21 @@
|
||||
Index: setup.py
|
||||
--- setup.py.orig
|
||||
+++ setup.py
|
||||
@@ -1,6 +1,6 @@
|
||||
@@ -1,6 +1,5 @@
|
||||
from distutils.core import Extension, setup
|
||||
from distutils.command.install import INSTALL_SCHEMES
|
||||
-from Pyrex.Distutils import build_ext
|
||||
+from Cython.Distutils import build_ext
|
||||
import os
|
||||
|
||||
# http://stackoverflow.com/questions/1612733/including-non-python-files-with-setup-py
|
||||
@@ -52,6 +52,5 @@ setup( name='MysticMine',
|
||||
@@ -45,13 +44,8 @@ setup( name='MysticMine',
|
||||
('monorail/data/music',music),
|
||||
('monorail/data/snd',snd),
|
||||
],
|
||||
- ext_modules=[
|
||||
- Extension("monorail.ai", ["monorail/ai.pyx"])
|
||||
- ],
|
||||
- cmdclass={'build_ext': build_ext},
|
||||
requires=[
|
||||
"pygame",
|
||||
"numpy",
|
||||
|
||||
@@ -3,7 +3,8 @@ lib/python${MODPY_VERSION}/site-packages/MysticMine-${MODPY_EGG_VERSION}-py${MOD
|
||||
lib/python${MODPY_VERSION}/site-packages/monorail/
|
||||
lib/python${MODPY_VERSION}/site-packages/monorail/__init__.py
|
||||
lib/python${MODPY_VERSION}/site-packages/monorail/__init__.pyc
|
||||
@so lib/python${MODPY_VERSION}/site-packages/monorail/ai.so
|
||||
lib/python${MODPY_VERSION}/site-packages/monorail/ai.py
|
||||
lib/python${MODPY_VERSION}/site-packages/monorail/ai.pyc
|
||||
lib/python${MODPY_VERSION}/site-packages/monorail/control.py
|
||||
lib/python${MODPY_VERSION}/site-packages/monorail/control.pyc
|
||||
lib/python${MODPY_VERSION}/site-packages/monorail/controlview.py
|
||||
|
||||
Reference in New Issue
Block a user