diff --git a/Making a Wargame.ipynb b/Making a Wargame.ipynb new file mode 100644 index 0000000..4589815 --- /dev/null +++ b/Making a Wargame.ipynb @@ -0,0 +1,1154 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We want to make a turn-based strategy game, based on a **hexagonal grid**.\n", + "\n", + "Wait, how do we even lay out a hexagonal grid in kivy?\n", + "\n", + " http://playtechs.blogspot.com/2007/04/hex-grids.html\n", + " \n", + " Also\n", + " \n", + " http://gamedev.stackexchange.com/questions/15881/hexagonal-grid-tiles-tutorials\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ignore device orientation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's lay out a grid. First we need to draw hexagons. Clone gamecamp. change the object names.\n", + "Can we lay out the basic screen?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%%file strategygame.kv\n", + "\n", + "#:include debug.kv\n", + "\n", + ":\n", + " BoxLayout:\n", + " orientation: 'horizontal'\n", + " DebugLabel:\n", + " text: 'Main map'\n", + " size_hint: .75, 1\n", + " BoxLayout:\n", + " orientation: 'vertical'\n", + " size_hint: .25, 1\n", + " DebugLabel:\n", + " text: 'status'\n", + " size_hint: 1, .66\n", + " DebugLabel:\n", + " text: 'mini-map'\n", + " size_hint: 1, .33\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%%file main.py\n", + "from kivy.app import App\n", + "from kivy.uix.floatlayout import FloatLayout\n", + "\n", + "\n", + "class StrategyGame(FloatLayout):\n", + " pass\n", + "\n", + "\n", + "class StrategyGameApp(App):\n", + " def build(self):\n", + " return StrategyGame()\n", + "\n", + "if __name__ == '__main__':\n", + " StrategyGameApp().run()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "#!python main.py\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's make a gridlayout in the game area, and give it a label so we can refer to it from our code. We are going to build a rectangular grid" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%%file strategygame.kv\n", + "\n", + "#:include debug.kv\n", + "\n", + ":\n", + " main_map: _main_map\n", + " BoxLayout:\n", + " orientation: 'horizontal'\n", + " GridLayout:\n", + " id: _main_map\n", + " cols: 10\n", + " size_hint: .75, 1\n", + " BoxLayout:\n", + " orientation: 'vertical'\n", + " size_hint: .25, 1\n", + " DebugLabel:\n", + " text: 'status'\n", + " size_hint: 1, .66\n", + " DebugLabel:\n", + " text: 'mini-map'\n", + " size_hint: 1, .33\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's hook to that from code. We will do this in `__init__` in our main StrategyGame class. Let's make a loop to add buttons to the root widget. We will use properties in the kv file to determine how many rows/cols to add. We will also need to refer to various objects in the widget hierarchy in the code." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can refer to the top-level object (non-indented) as `root` in the kv file, but it we want to refer to conveniently elsewhere in the code, we need to use an id.\n", + "\n", + "`id: _game`\n", + "\n", + "and then from any widget we want to refer to it, we do a \n", + "\n", + "`game: _game`\n", + "\n", + "We'll use this to access the number of regions to layout in the grid via `map_cols` and `map_rows` from the main game widget. \n", + "\n", + "Fill in grids: it fills it in by default, left to right, top to bottom, and give them coordinate references. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%%file strategygame.kv\n", + "\n", + "#:include debug.kv\n", + "\n", + ":\n", + " id: _game\n", + " main_map: _main_map\n", + " map_rows: 10\n", + " map_cols: 10\n", + " BoxLayout:\n", + " orientation: 'horizontal'\n", + " GridLayout:\n", + " id: _main_map\n", + " game: _game\n", + " cols: root.map_cols\n", + " size_hint: .75, 1\n", + " BoxLayout:\n", + " orientation: 'vertical'\n", + " size_hint: .25, 1\n", + " DebugLabel:\n", + " text: 'status'\n", + " size_hint: 1, .66\n", + " DebugLabel:\n", + " text: 'mini-map'\n", + " size_hint: 1, .33\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# %load main.py\n", + "from kivy.app import App\n", + "from kivy import properties\n", + "from kivy.uix import button\n", + "from kivy.uix.floatlayout import FloatLayout\n", + "\n", + "\n", + "class StrategyGame(FloatLayout):\n", + " main_map = properties.ObjectProperty(None)\n", + " map_rows = properties.NumericProperty(0)\n", + " map_cols = properties.NumericProperty(0)\n", + "\n", + " def __init__(self, **kwargs):\n", + " super(StrategyGame, self).__init__(**kwargs)\n", + "\n", + " number_of_regions = self.map_rows * self.map_cols\n", + " for region in xrange(0, number_of_regions):\n", + " row = region / self.map_cols\n", + " col = region % self.map_cols\n", + " self.main_map.add_widget(button.Button(text='({}, {})'.format(row, col)))\n", + "\n", + "\n", + "class StrategyGameApp(App):\n", + " def build(self):\n", + " return StrategyGame()\n", + "\n", + "if __name__ == '__main__':\n", + " StrategyGameApp().run()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We draw alternating rows with the patterns:\n", + "```\n", + "/ \\\n", + "| |\n", + "| |\n", + "```\n", + "and\n", + "```\n", + "\\ /\n", + " |\n", + " |\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%%file strategygame.kv\n", + "\n", + "#:include debug.kv\n", + "\n", + ":\n", + " id: _game\n", + " main_map: _main_map\n", + " map_rows: 10\n", + " map_cols: 10\n", + " BoxLayout:\n", + " orientation: 'horizontal'\n", + " GridLayout:\n", + " id: _main_map\n", + " game: _game\n", + " cols: root.map_cols\n", + " size_hint: .75, 1\n", + " BoxLayout:\n", + " orientation: 'vertical'\n", + " size_hint: .25, 1\n", + " DebugLabel:\n", + " text: 'status'\n", + " size_hint: 1, .66\n", + " DebugLabel:\n", + " text: 'mini-map'\n", + " size_hint: 1, .33\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%%file main.py\n", + "from kivy.app import App\n", + "from kivy import properties\n", + "from kivy.uix import button\n", + "from kivy.uix.floatlayout import FloatLayout\n", + "\n", + "\n", + "class StrategyGame(FloatLayout):\n", + " main_map = properties.ObjectProperty(None)\n", + " map_rows = properties.NumericProperty(0)\n", + " map_cols = properties.NumericProperty(0)\n", + "\n", + " def __init__(self, **kwargs):\n", + " super(StrategyGame, self).__init__(**kwargs)\n", + "\n", + " number_of_regions = self.map_rows * self.map_cols\n", + " for region in xrange(0, number_of_regions):\n", + " row = region / self.map_cols\n", + " col = region % self.map_cols\n", + " self.main_map.add_widget(button.Button(text='({}, {})'.format(row, col)))\n", + "\n", + "\n", + "class StrategyGameApp(App):\n", + " def build(self):\n", + " return StrategyGame()\n", + "\n", + "if __name__ == '__main__':\n", + " StrategyGameApp().run()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wait, that doesn't draw where we think it should.'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%%file main.py\n", + "import collections\n", + "\n", + "from kivy.app import App\n", + "from kivy import properties\n", + "from kivy import graphics\n", + "from kivy.uix import label\n", + "from kivy.uix.floatlayout import FloatLayout\n", + "\n", + "\n", + "MapCoords = collections.namedtuple('MapCoords', ['row', 'col'])\n", + "\n", + "\n", + "class StrategyGame(FloatLayout):\n", + " main_map = properties.ObjectProperty(None)\n", + " map_rows = properties.NumericProperty(0)\n", + " map_cols = properties.NumericProperty(0)\n", + "\n", + " def __init__(self, **kwargs):\n", + " super(StrategyGame, self).__init__(**kwargs)\n", + "\n", + " number_of_regions = self.map_rows * self.map_cols\n", + " for region in xrange(0, number_of_regions):\n", + " row = region / self.map_cols\n", + " col = region % self.map_cols\n", + " self.main_map.add_widget(HexMapCell(row=row, col=col))\n", + "\n", + "\n", + "class HexMapCell(label.Label):\n", + " def __init__(self, row=0, col=0, **kwargs):\n", + " self.region_in_map = MapCoords(row, col)\n", + " super(HexMapCell, self).__init__(**kwargs)\n", + " self.draw_hex_edge()\n", + "\n", + " def draw_hex_edge(self):\n", + " edge = ''\n", + " if self.region_in_map.col % 2 == 0:\n", + " row_mod = self.region_in_map.row % 6\n", + " if row_mod == 0:\n", + " edge = 'BU'\n", + " with self.canvas:\n", + " graphics.Color(1, 1, 1, 1)\n", + " graphics.Line(points=[self.x, self.y, self.width + self.x, self.height + self.y])\n", + " elif row_mod in (1, 2):\n", + " edge = 'L '\n", + " elif row_mod == 3:\n", + " edge = 'TD'\n", + " elif row_mod in (4, 5):\n", + " edge = ' R'\n", + " else:\n", + " row_mod = self.region_in_map.row % 6\n", + " if row_mod == 0:\n", + " edge = 'TD'\n", + " elif row_mod in (1, 2):\n", + " edge = ' R'\n", + " elif row_mod == 3:\n", + " edge = 'BU'\n", + " elif row_mod in (4, 5):\n", + " edge = 'L '\n", + "\n", + " self.text = edge\n", + "\n", + "class StrategyGameApp(App):\n", + " def build(self):\n", + " return StrategyGame()\n", + "\n", + "if __name__ == '__main__':\n", + " StrategyGameApp().run()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%%file main.py\n", + "from kivy.app import App\n", + "from kivy.uix.gridlayout import GridLayout\n", + "from kivy.lang import Builder\n", + "from kivy.properties import NumericProperty\n", + "\n", + "class HelloWorld(GridLayout):\n", + " cols = NumericProperty(4)\n", + " def __init__(self, **kw):\n", + " super(HelloWorld, self).__init__(**kw)\n", + " self.add_widget(self.BU())\n", + " self.add_widget(self.TD())\n", + " self.add_widget(self.BU())\n", + " self.add_widget(self.TD())\n", + "\n", + " def BU(self):\n", + " return Builder.load_string('''\n", + "Label:\n", + " canvas:\n", + " Color:\n", + " rgba: (1,1,1,1)\n", + " Line:\n", + " points: (self.x, self.y, self.right, self.top)\n", + " width: 2''')\n", + "\n", + " def TD(self):\n", + " return Builder.load_string('''\n", + "Label:\n", + " canvas:\n", + " Color:\n", + " rgba: (1,1,1,1)\n", + " Line:\n", + " points: (self.x, self.top, self.right, self.y)\n", + " width: 2''')\n", + "\n", + " def L(self):\n", + " return Builder.load_string('''\n", + "Label:\n", + " canvas:\n", + " Color:\n", + " rgba: (1,1,1,1)\n", + " Line:\n", + " points: (self.x, self.y, self.x, self.top)\n", + " width: 2''')\n", + "\n", + " def R(self):\n", + " return Builder.load_string('''\n", + "Label:\n", + " canvas:\n", + " Color:\n", + " rgba: (1,1,1,1)\n", + " Line:\n", + " points: (self.right, self.y, self.right, self.top)\n", + " width: 2''')\n", + "\n", + "\n", + "class HelloWorldApp(App):\n", + " def build(self):\n", + " return HelloWorld()\n", + "\n", + "if __name__ == '__main__':\n", + " HelloWorldApp().run()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# %load strategygame.kv\n", + "\n", + "#:include debug.kv\n", + "\n", + ":\n", + " id: _game\n", + " main_map: _main_map\n", + " map_rows: 10\n", + " map_cols: 10\n", + " BoxLayout:\n", + " orientation: 'horizontal'\n", + " GridLayout:\n", + " id: _main_map\n", + " game: _game\n", + " cols: root.map_cols\n", + " size_hint: .75, 1\n", + " BoxLayout:\n", + " orientation: 'vertical'\n", + " size_hint: .25, 1\n", + " DebugLabel:\n", + " text: 'status'\n", + " size_hint: 1, .66\n", + " DebugLabel:\n", + " text: 'mini-map'\n", + " size_hint: 1, .33\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "## Trying to draw the hexmap\n", + "\n", + "We tried adding the ability to style our widgets for to draw the Hexmap using lines like this\n", + "\n", + " BU: /\n", + " TD: \\\n", + " L: |\n", + " R: |\n", + "We get hexs now, but we need to figure out how to do the aspect ratio properly now...for a spiky top, we need each grid widget to have aspec ratio $$\\sqrt{3}\\cdot height = width$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%%file strategygame.kv\n", + "\n", + "#:include debug.kv\n", + "\n", + ":\n", + " id: _game\n", + " main_map: _main_map\n", + " map_rows: 10\n", + " map_cols: 10\n", + " BoxLayout:\n", + " orientation: 'horizontal'\n", + " GridLayout:\n", + " id: _main_map\n", + " game: _game\n", + " cols: root.map_cols\n", + " size_hint: .75, 1\n", + " BoxLayout:\n", + " orientation: 'vertical'\n", + " size_hint: .25, 1\n", + " DebugLabel:\n", + " text: 'status'\n", + " size_hint: 1, .66\n", + " DebugLabel:\n", + " text: 'mini-map'\n", + " size_hint: 1, .33\n", + "\n", + ":\n", + " canvas:\n", + " Color:\n", + " rgba: (1,1,1,1)\n", + " Line:\n", + " points: (self.x, self.y, self.right, self.top)\n", + " width: 2\n", + ":\n", + " canvas:\n", + " Color:\n", + " rgba: (1,1,1,1)\n", + " Line:\n", + " points: (self.x, self.top, self.right, self.y)\n", + " width: 2\n", + ":\n", + " canvas:\n", + " Color:\n", + " rgba: (1,1,1,1)\n", + " Line:\n", + " points: (self.x, self.y, self.x, self.top)\n", + " width: 2\n", + ":\n", + " canvas:\n", + " Color:\n", + " rgba: (1,1,1,1)\n", + " Line:\n", + " points: (self.right, self.y, self.right, self.top)\n", + " width: 2\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%%file main.py\n", + "import collections\n", + "\n", + "from kivy.app import App\n", + "from kivy import properties\n", + "from kivy import graphics\n", + "from kivy.uix import label\n", + "from kivy.uix.floatlayout import FloatLayout\n", + "import math\n", + "\n", + "MapCoords = collections.namedtuple('MapCoords', ['row', 'col'])\n", + "\n", + "\n", + "class StrategyGame(FloatLayout):\n", + " main_map = properties.ObjectProperty(None)\n", + " map_rows = properties.NumericProperty(0)\n", + " map_cols = properties.NumericProperty(0)\n", + "\n", + " def __init__(self, **kwargs):\n", + " super(StrategyGame, self).__init__(**kwargs)\n", + "\n", + " number_of_regions = self.map_rows * self.map_cols\n", + " for region in xrange(0, number_of_regions):\n", + " row = region / self.map_cols\n", + " col = region % self.map_cols\n", + " self.main_map.add_widget(self.pick_hex_cell(row=row, col=col))\n", + "\n", + "\n", + " def pick_hex_cell(self, row, col):\n", + " row_mod = row % 6\n", + " if col % 2 == 0:\n", + " if row_mod == 0:\n", + " return BU()\n", + " elif row_mod in (1, 2):\n", + " return L()\n", + " elif row_mod == 3:\n", + " return TD()\n", + " elif row_mod in (4, 5):\n", + " return R()\n", + " else:\n", + " if row_mod == 0:\n", + " return TD()\n", + " elif row_mod in (1, 2):\n", + " return R()\n", + " elif row_mod == 3:\n", + " return BU()\n", + " elif row_mod in (4, 5):\n", + " return L()\n", + "\n", + "\n", + "class HexMapCell(label.Label):\n", + " def __init__(self, row=0, col=0, **kwargs):\n", + " self.region_in_map = MapCoords(row, col)\n", + " super(HexMapCell, self).__init__(**kwargs)\n", + "\n", + "class BU(HexMapCell):\n", + " pass\n", + "class TD(HexMapCell):\n", + " pass\n", + "class L(HexMapCell):\n", + " pass\n", + "class R(HexMapCell):\n", + " pass\n", + "\n", + "\n", + "class StrategyGameApp(App):\n", + " def build(self):\n", + " return StrategyGame()\n", + "\n", + "if __name__ == '__main__':\n", + " StrategyGameApp().run()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "Replaced the debug labels and coloured the status and minimap two different ways. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%%file strategygame.kv\n", + "#:import math math\n", + "\n", + ":\n", + " id: _game\n", + " main_map: _main_map\n", + " map_rows: 30\n", + " map_cols: 10\n", + " BoxLayout:\n", + " orientation: 'horizontal'\n", + " GridLayout:\n", + " id: _main_map\n", + " game: _game\n", + " cols: root.map_cols\n", + " size_hint: .75, 1\n", + " BoxLayout:\n", + " orientation: 'vertical'\n", + " size_hint: .25, 1\n", + " Label:\n", + " id: _stats\n", + " text: 'status'\n", + " size_hint: 1, .66\n", + " canvas.before:\n", + " Color:\n", + " rgba: .49, .49, .81, 1\n", + " Rectangle:\n", + " pos: _stats.pos\n", + " size: _stats.size\n", + " Button:\n", + " text: 'mini-map'\n", + " size_hint: 1, .33\n", + " background_color: .75, .71, .99, 1\n", + "\n", + "\n", + ":\n", + " canvas:\n", + " Color:\n", + " rgba: (1,1,1,1)\n", + " Line:\n", + " points: (self.x, self.y, self.right, self.top)\n", + " width: 2\n", + ":\n", + " canvas:\n", + " Color:\n", + " rgba: (1,1,1,1)\n", + " Line:\n", + " points: (self.x, self.top, self.right, self.y)\n", + " width: 2\n", + ":\n", + " canvas:\n", + " Color:\n", + " rgba: (1,1,1,1)\n", + " Line:\n", + " points: (self.x, self.y, self.x, self.top)\n", + " width: 2\n", + ":\n", + " canvas:\n", + " Color:\n", + " rgba: (1,1,1,1)\n", + " Line:\n", + " points: (self.right, self.y, self.right, self.top)\n", + " width: 2\n", + "\n", + ":\n", + " size_hint: 1, None\n", + " height: self.width / math.sqrt(3)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%%file main.py\n", + "import collections\n", + "import random\n", + "\n", + "from kivy import app, properties\n", + "from kivy.uix import button, label\n", + "from kivy.uix.floatlayout import FloatLayout\n", + "\n", + "MapCoords = collections.namedtuple('MapCoords', ['row', 'col'])\n", + "\n", + "\n", + "class StrategyGame(FloatLayout):\n", + " main_map = properties.ObjectProperty(None)\n", + " map_rows = properties.NumericProperty(0)\n", + " map_cols = properties.NumericProperty(0)\n", + "\n", + " def __init__(self, **kwargs):\n", + " super(StrategyGame, self).__init__(**kwargs)\n", + "\n", + " number_of_regions = self.map_rows * self.map_cols\n", + " for region in xrange(0, number_of_regions):\n", + " row = region / self.map_cols\n", + " col = region % self.map_cols\n", + "\n", + " # Add hex cells to make up the map.\n", + " hex_cell = self.pick_hex_cell(row=row, col=col)\n", + " self.main_map.add_widget(hex_cell)\n", + "\n", + " # Add overlay conditionally.\n", + " if (row % 6 == 2 and col % 2 == 0) or (row % 6 == 5 and col % 2 == 1):\n", + " print('({}, {})'.format(row, col))\n", + " self.add_widget(HexMapControlCell(hex_bind=hex_cell))\n", + "\n", + " @staticmethod\n", + " def pick_hex_cell(row, col):\n", + " row_mod = row % 6\n", + " if col % 2 == 0:\n", + " if row_mod == 0:\n", + " return BU()\n", + " elif row_mod in (1, 2):\n", + " return L()\n", + " elif row_mod == 3:\n", + " return TD()\n", + " elif row_mod in (4, 5):\n", + " return R()\n", + " else:\n", + " if row_mod == 0:\n", + " return TD()\n", + " elif row_mod in (1, 2):\n", + " return R()\n", + " elif row_mod == 3:\n", + " return BU()\n", + " elif row_mod in (4, 5):\n", + " return L()\n", + "\n", + "\n", + "class HexMapCell(label.Label):\n", + " def __init__(self, row=0, col=0, **kwargs):\n", + " super(HexMapCell, self).__init__(**kwargs)\n", + " self.coords = MapCoords(row, col)\n", + "\n", + "\n", + "class BU(HexMapCell):\n", + " pass\n", + "\n", + "\n", + "class TD(HexMapCell):\n", + " pass\n", + "\n", + "\n", + "class L(HexMapCell):\n", + " pass\n", + "\n", + "\n", + "class R(HexMapCell):\n", + " pass\n", + "\n", + "\n", + "class HexMapControlCell(button.Button):\n", + " def __init__(self, hex_bind=None, **kwargs):\n", + " super(HexMapControlCell, self).__init__(**kwargs)\n", + " self.hex_bind = hex_bind\n", + " self.background_color = random.random(), random.random(), random.random(), 1\n", + " self.bind(pos=self.reposition_control_cell, size=self.resize_control_cell)\n", + " self.text = '({}, {})'.format(self.hex_bind.coords.row, self.hex_bind.coords.col)\n", + "\n", + " def reposition_control_cell(self, obj, value):\n", + " self.pos = self.hex_bind.pos\n", + "\n", + " def resize_control_cell(self, obj, value):\n", + " self.height = self.hex_bind.height * 2\n", + " self.width = self.hex_bind.width * 2\n", + "\n", + "\n", + "class StrategyGameApp(app.App):\n", + " def build(self):\n", + " return StrategyGame()\n", + "\n", + "if __name__ == '__main__':\n", + " StrategyGameApp().run()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Warning** if you run this your will get an error!!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "We should probably have a viewer and a hexagon map underneath!!\n", + "\n", + "But not for now...let's make an overlay of boxes...and label them with an \"offset-r)\n", + "\n", + "Hmmmm....overlay...is...having...issues. \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%%file strategygame.kv\n", + "#:import math math\n", + "#:include debug.kv\n", + "\n", + ":\n", + " id: _game\n", + " main_map: _main_map\n", + " map_rows: 30\n", + " map_cols: 10\n", + " BoxLayout:\n", + " orientation: 'horizontal'\n", + " GridLayout:\n", + " id: _main_map\n", + " game: _game\n", + " cols: root.map_cols\n", + " size_hint: .75, 1\n", + " BoxLayout:\n", + " orientation: 'vertical'\n", + " size_hint: .25, 1\n", + " Label:\n", + " id: _stats\n", + " text: 'status'\n", + " size_hint: 1, .66\n", + " canvas.before:\n", + " Color:\n", + " rgba: .49, .49, .81, 1\n", + " Rectangle:\n", + " pos: _stats.pos\n", + " size: _stats.size\n", + " Button:\n", + " text: 'mini-map'\n", + " size_hint: 1, .33\n", + " background_color: .75, .71, .99, 1\n", + "\n", + ":\n", + " pos_hint: {'center_x':.5, 'center_y':.5}\n", + " canvas.after:\n", + " Color:\n", + " rgba: 1,1,1,1\n", + " Ellipse:\n", + " segments: 6\n", + " pos: self.pos\n", + " size: min(self.width, self.height), min(self.width, self.height)\n", + "\n", + ":\n", + " size_hint: 1, None\n", + " height: self.width / math.sqrt(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%%file main.py\n", + "import collections\n", + "import random\n", + "import math\n", + "from kivy import app, properties\n", + "from kivy.uix import button, label\n", + "from kivy.uix.floatlayout import FloatLayout\n", + "from kivy.graphics import Color, Ellipse, Line\n", + "from kivy.logger import Logger\n", + "\n", + "MapCoords = collections.namedtuple('MapCoords', ['row', 'col'])\n", + "\n", + "\n", + "class StrategyGame(FloatLayout):\n", + " main_map = properties.ObjectProperty(None)\n", + " map_rows = properties.NumericProperty(0)\n", + " map_cols = properties.NumericProperty(0)\n", + "\n", + " def __init__(self, **kwargs):\n", + " super(StrategyGame, self).__init__(**kwargs)\n", + "\n", + " number_of_regions = self.map_rows * self.map_cols\n", + " for region in xrange(0, number_of_regions):\n", + " row = region / self.map_cols\n", + " col = region % self.map_cols\n", + "\n", + " # Add hex cells to make up the map.\n", + " hex_cell = self.pick_hex_cell(row=row, col=col)\n", + " self.main_map.add_widget(hex_cell)\n", + "\n", + " # Add overlay conditionally.\n", + " if (row % 6 == 1 and col % 2 == 1) or (row % 6 == 4 and col % 2 == 0):\n", + " print('({}, {})'.format(row, col))\n", + " #radius = math.sqrt(hex_cell.width**2 + hex_cell.height**2)\n", + " radius = 2*hex_cell.height\n", + " with hex_cell.canvas.after:\n", + " Color(1,0,1,1)\n", + " hex_cell.ell = Line(circle=(hex_cell.x, hex_cell.y,radius, 0, 360, 6), width=2)\n", + " hex_cell.bind(pos=hex_cell.update_pos, size=hex_cell.update_pos)\n", + "\n", + "\n", + " @staticmethod\n", + " def pick_hex_cell(row, col):\n", + " row_mod = row % 6\n", + " if col % 2 == 0:\n", + " if row_mod == 0:\n", + " return BU()\n", + " elif row_mod in (1, 2):\n", + " return L()\n", + " elif row_mod == 3:\n", + " return TD()\n", + " elif row_mod in (4, 5):\n", + " return R()\n", + " else:\n", + " if row_mod == 0:\n", + " return TD()\n", + " elif row_mod in (1, 2):\n", + " return R()\n", + " elif row_mod == 3:\n", + " return BU()\n", + " elif row_mod in (4, 5):\n", + " return L()\n", + "\n", + "\n", + "\n", + "class HexMapCell(label.Label):\n", + " def __init__(self, row=0, col=0, **kwargs):\n", + " super(HexMapCell, self).__init__(**kwargs)\n", + " self.coords = MapCoords(row, col)\n", + "\n", + " def update_pos(self, instance, value):\n", + " Logger.info(\"StratGame: {}\".format(instance))\n", + " #radius = math.sqrt(self.width**2 + self.height**2)\n", + " radius = 2*self.height\n", + " self.ell.circle = (self.x, self.y, radius, 0, 360, 6)\n", + "\n", + "\n", + "\n", + "class BU(HexMapCell):\n", + " pass\n", + "\n", + "\n", + "class TD(HexMapCell):\n", + " pass\n", + "\n", + "\n", + "class L(HexMapCell):\n", + " pass\n", + "\n", + "\n", + "class R(HexMapCell):\n", + " pass\n", + "\n", + "\n", + "\n", + "class HexMapControlCell(button.Button):\n", + " def __init__(self, hex_bind=None, **kwargs):\n", + " super(HexMapControlCell, self).__init__(**kwargs)\n", + " self.hex_bind = hex_bind\n", + " self.background_color = random.random(), random.random(), random.random(), 1\n", + " self.bind(pos=self.reposition_control_cell, size=self.resize_control_cell)\n", + " self.text = '({}, {})'.format(self.hex_bind.coords.row, self.hex_bind.coords.col)\n", + "\n", + " def reposition_control_cell(self, obj, value):\n", + " self.pos = self.hex_bind.pos\n", + "\n", + " def resize_control_cell(self, obj, value):\n", + " self.height = self.hex_bind.height * 2\n", + " self.width = self.hex_bind.width * 2\n", + "\n", + "\n", + "class StrategyGameApp(app.App):\n", + " def build(self):\n", + " return StrategyGame()\n", + "\n", + "if __name__ == '__main__':\n", + " StrategyGameApp().run()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What did we learn? \n", + "\n", + "* angle 0 appears to be straight up for a circle or ellipse\n", + "* The radius of the hexagon is $2*height$ for a pointed top hexagon, and for flat top, the radius of the hexagon is $2*width$\n", + "* Make sure you fix the aspect ratio of the rectangles you're basing things now\n", + "* segments are inscribed **inside** the circle\n", + "* tiling problems are hard!!! get a mathematician.\n", + "* there is a utility for hex codes for colours\n", + "* draw the hexagon using a Line with a circle to make an outline instead of an Ellipse\n", + "\n", + "Cleaned some stuff up and added colours to the hexagons. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.11" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/helloworld.kv b/helloworld.kv deleted file mode 100644 index 97643d1..0000000 --- a/helloworld.kv +++ /dev/null @@ -1,6 +0,0 @@ - -#:include debug.kv - -: - DebugLabel: - text: 'hello world' \ No newline at end of file diff --git a/main.py b/main.py index a437177..fb38a99 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,85 @@ -from kivy.app import App +import collections +import random +import math + +from kivy import app, properties +from kivy.uix import button, label from kivy.uix.floatlayout import FloatLayout +from kivy.graphics import Color, Ellipse, Line +from kivy.logger import Logger +import kivy.utils +from kivy.vector import Vector -class HelloWorld(FloatLayout): - pass +MapCoords = collections.namedtuple('MapCoords', ['row', 'col']) -class HelloWorldApp(App): + +class StrategyGame(FloatLayout): + main_map = properties.ObjectProperty(None) + map_rows = properties.NumericProperty(0) + map_cols = properties.NumericProperty(0) + + def __init__(self, **kwargs): + super(StrategyGame, self).__init__(**kwargs) + + number_of_regions = self.map_rows * self.map_cols + for region in xrange(0, number_of_regions): + row = region / self.map_cols + col = region % self.map_cols + + # Add hex cells to make up the map. + hex_cell = HexMapCell() + self.main_map.add_widget(hex_cell) + + # Add overlay conditionally. + if (row % 6 == 1 and col % 2 == 1) or (row % 6 == 4 and col % 2 == 0): + print('({}, {})'.format(row, col)) + #radius = math.sqrt(hex_cell.width**2 + hex_cell.height**2) + radius = 2*hex_cell.height + solid_x = hex_cell.x - hex_cell.height*2 + solid_y = hex_cell.y - hex_cell.height*2 + solid_size = (4*hex_cell.height, 4*hex_cell.height) + with hex_cell.canvas.after: + Color(1,0,1,1) + hex_cell.ell = Line(circle=(hex_cell.x, hex_cell.y,radius, 0, 360, 6), width=2) + Color(*kivy.utils.get_random_color(alpha = .5)) + hex_cell.solid = Ellipse(pos = (solid_x, solid_y), size = solid_size, segments = 6 ) + hex_cell.bind(pos=hex_cell.update_pos, size=hex_cell.update_pos) + + +class HexMapCell(label.Label): + def __init__(self, row=0, col=0, **kwargs): + super(HexMapCell, self).__init__(**kwargs) + self.coords = MapCoords(row, col) + + def update_pos(self, instance, value): + #radius = math.sqrt(self.width**2 + self.height**2) + radius = 2*self.height + solid_x = self.x - self.height*2 + solid_y = self.y - self.height*2 + solid_size = (4*self.height, 4*self.height) + self.ell.circle = (self.x, self.y, radius, 0, 360, 6) + self.solid.pos = (solid_x, solid_y) + self.solid.size = solid_size + +class HexMapControlCell(button.Button): + def __init__(self, hex_bind=None, **kwargs): + super(HexMapControlCell, self).__init__(**kwargs) + self.hex_bind = hex_bind + self.background_color = random.random(), random.random(), random.random(), 1 + self.bind(pos=self.reposition_control_cell, size=self.resize_control_cell) + self.text = '({}, {})'.format(self.hex_bind.coords.row, self.hex_bind.coords.col) + + def reposition_control_cell(self, obj, value): + self.pos = self.hex_bind.pos + + def resize_control_cell(self, obj, value): + self.height = self.hex_bind.height * 2 + self.width = self.hex_bind.width * 2 + + +class StrategyGameApp(app.App): def build(self): - return HelloWorld() + return StrategyGame() if __name__ == '__main__': - HelloWorldApp().run() + StrategyGameApp().run() diff --git a/strategygame.kv b/strategygame.kv new file mode 100644 index 0000000..886890a --- /dev/null +++ b/strategygame.kv @@ -0,0 +1,46 @@ +#:import math math +##:include debug.kv + +: + id: _game + main_map: _main_map + map_rows: 30 + map_cols: 10 + BoxLayout: + orientation: 'horizontal' + GridLayout: + id: _main_map + game: _game + cols: root.map_cols + size_hint: .75, 1 + BoxLayout: + orientation: 'vertical' + size_hint: .25, 1 + Label: + id: _stats + text: 'status' + size_hint: 1, .66 + canvas.before: + Color: + rgba: .49, .49, .81, 1 + Rectangle: + pos: _stats.pos + size: _stats.size + Button: + text: 'mini-map' + size_hint: 1, .33 + background_color: .75, .71, .99, 1 + +: + pos_hint: {'center_x':.5, 'center_y':.5} + canvas.after: + Color: + rgba: 1,1,1,1 + Ellipse: + segments: 6 + pos: self.pos + size: min(self.width, self.height), min(self.width, self.height) + +: + size_hint: 1, None + height: self.width / math.sqrt(3) \ No newline at end of file