├── objs ├── __init__.py ├── __pycache__ │ ├── Car.cpython-35.pyc │ ├── Car.cpython-36.pyc │ ├── Car.cpython-37.pyc │ ├── CarAI.cpython-35.pyc │ ├── CarAI.cpython-36.pyc │ ├── CarAI.cpython-37.pyc │ ├── Barrier.cpython-36.pyc │ ├── __init__.cpython-35.pyc │ ├── __init__.cpython-36.pyc │ ├── __init__.cpython-37.pyc │ ├── kivyObjs.cpython-35.pyc │ ├── kivyObjs.cpython-36.pyc │ ├── kivyObjs.cpython-37.pyc │ ├── GameObjects.cpython-35.pyc │ ├── GameObjects.cpython-36.pyc │ └── GameObjects.cpython-37.pyc ├── Car.py ├── GameObjects.py ├── kivyObjs.py └── CarAI.py ├── templates ├── options.kv ├── mainmenu.kv └── canvas.kv ├── windows ├── __init__.py ├── __pycache__ │ ├── Level.cpython-36.pyc │ ├── Level.cpython-37.pyc │ ├── PopNot.cpython-35.pyc │ ├── PopNot.cpython-36.pyc │ ├── PopNot.cpython-37.pyc │ ├── MainWindow.cpython-36.pyc │ ├── Simulation.cpython-35.pyc │ ├── Simulation.cpython-36.pyc │ ├── Simulation.cpython-37.pyc │ ├── __init__.cpython-35.pyc │ ├── __init__.cpython-36.pyc │ ├── __init__.cpython-37.pyc │ ├── CanvasWindow.cpython-35.pyc │ ├── CanvasWindow.cpython-36.pyc │ ├── CanvasWindow.cpython-37.pyc │ ├── GameWidgets.cpython-35.pyc │ ├── GameWidgets.cpython-36.pyc │ ├── GameWidgets.cpython-37.pyc │ ├── ImportExport.cpython-35.pyc │ ├── ImportExport.cpython-36.pyc │ ├── ImportExport.cpython-37.pyc │ ├── LayersWindow.cpython-35.pyc │ ├── LayersWindow.cpython-36.pyc │ ├── LayersWindow.cpython-37.pyc │ ├── CanvasHandler.cpython-35.pyc │ ├── CanvasHandler.cpython-36.pyc │ └── CanvasHandler.cpython-37.pyc ├── PopNot.py ├── CanvasWindow.py ├── LayersWindow.py ├── ImportExport.py ├── Simulation.py └── GameWidgets.py ├── NeuralApp_mem_profiled.py ├── .gitattributes ├── pic.png ├── icon.png ├── .vs ├── slnx.sqlite ├── NeuralSandbox2 │ └── v16 │ │ └── .suo ├── PythonSettings.json └── VSWorkspaceState.json ├── levels ├── maze.lvl ├── conti.lvl ├── loopy.lvl ├── snake.lvl ├── contiHard.lvl └── theMaster.lvl ├── docs ├── dias │ ├── dqn.dia │ ├── mlp.dia │ ├── dqn.dia~ │ ├── mlp.dia~ │ ├── perceptron.dia │ ├── perceptron.dia~ │ ├── supervised.dia │ ├── supervised.dia~ │ ├── futurereward.dia │ ├── futurereward.dia~ │ └── reinforcement.dia ├── imgs │ ├── dqn.png │ ├── mlp.png │ ├── relu.png │ ├── step.png │ ├── epsilon.png │ ├── raycasts.png │ ├── senzory.png │ ├── logisticka.png │ ├── modelinfo.png │ ├── supervised.png │ ├── editaddpanels.png │ ├── editnetowork.png │ ├── futurereward.png │ ├── leftrightdep.png │ ├── reinforcement.png │ ├── K-means visual.png │ ├── perceptron-dia.png │ ├── perceptron-sl-data.png │ └── perceptron-sl-predicted.png ├── scripts │ ├── Plots │ │ ├── model.h5 │ │ ├── model_big.h5 │ │ ├── model_conti.h5 │ │ ├── relu.py │ │ ├── logistic.py │ │ ├── step.py │ │ └── choicePlot.py │ └── PerceptronPointClassification │ │ └── main.py └── dokumentace_projektu_cs.pdf ├── icons ├── dangericon.png ├── gameicon.png ├── infoicon.png ├── levelicon.png └── warningicon.png ├── networks ├── 24conti.h5 ├── 29snake.h5 ├── 300conti.h5 ├── 1610conti.h5 ├── 35themaster.h5 ├── 100contiMaster-35-3.h5 └── 9layer255contir3-35.h5 ├── textures └── carTexture.png ├── __pycache__ ├── config.cpython-35.pyc ├── config.cpython-36.pyc ├── NeuralApp.cpython-35.pyc └── NeuralApp.cpython-36.pyc ├── ai ├── __pycache__ │ ├── DQN.cpython-35.pyc │ ├── DQN.cpython-36.pyc │ ├── DQN.cpython-37.pyc │ ├── SGA.cpython-35.pyc │ ├── SGA.cpython-36.pyc │ ├── SGA.cpython-37.pyc │ ├── bank.cpython-36.pyc │ ├── bank.cpython-37.pyc │ ├── brain.cpython-36.pyc │ ├── Models.cpython-37.pyc │ ├── SeqGen.cpython-35.pyc │ ├── SeqGen.cpython-36.pyc │ ├── SeqGen.cpython-37.pyc │ ├── models.cpython-35.pyc │ ├── models.cpython-36.pyc │ ├── GameController.cpython-35.pyc │ ├── GameController.cpython-36.pyc │ ├── GameController.cpython-37.pyc │ └── TrainController.cpython-36.pyc ├── bank.py ├── SeqGen.py ├── models.py ├── SGA.py ├── DQN.py └── GameController.py ├── .gitignore ├── neural.ini ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── requirements_linux.txt ├── requirements.txt ├── README.md ├── NeuralTester.spec ├── config.py ├── CODE_OF_CONDUCT.md ├── remarks └── NeuralApp.py /objs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/options.kv: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /windows/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /NeuralApp_mem_profiled.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | 2 | * linguist-language=Python -------------------------------------------------------------------------------- /pic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/pic.png -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/icon.png -------------------------------------------------------------------------------- /.vs/slnx.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/.vs/slnx.sqlite -------------------------------------------------------------------------------- /levels/maze.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/levels/maze.lvl -------------------------------------------------------------------------------- /docs/dias/dqn.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/dias/dqn.dia -------------------------------------------------------------------------------- /docs/dias/mlp.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/dias/mlp.dia -------------------------------------------------------------------------------- /docs/imgs/dqn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/dqn.png -------------------------------------------------------------------------------- /docs/imgs/mlp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/mlp.png -------------------------------------------------------------------------------- /levels/conti.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/levels/conti.lvl -------------------------------------------------------------------------------- /levels/loopy.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/levels/loopy.lvl -------------------------------------------------------------------------------- /levels/snake.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/levels/snake.lvl -------------------------------------------------------------------------------- /docs/dias/dqn.dia~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/dias/dqn.dia~ -------------------------------------------------------------------------------- /docs/dias/mlp.dia~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/dias/mlp.dia~ -------------------------------------------------------------------------------- /docs/imgs/relu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/relu.png -------------------------------------------------------------------------------- /docs/imgs/step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/step.png -------------------------------------------------------------------------------- /icons/dangericon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/icons/dangericon.png -------------------------------------------------------------------------------- /icons/gameicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/icons/gameicon.png -------------------------------------------------------------------------------- /icons/infoicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/icons/infoicon.png -------------------------------------------------------------------------------- /icons/levelicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/icons/levelicon.png -------------------------------------------------------------------------------- /levels/contiHard.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/levels/contiHard.lvl -------------------------------------------------------------------------------- /levels/theMaster.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/levels/theMaster.lvl -------------------------------------------------------------------------------- /networks/24conti.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/networks/24conti.h5 -------------------------------------------------------------------------------- /networks/29snake.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/networks/29snake.h5 -------------------------------------------------------------------------------- /networks/300conti.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/networks/300conti.h5 -------------------------------------------------------------------------------- /docs/imgs/epsilon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/epsilon.png -------------------------------------------------------------------------------- /docs/imgs/raycasts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/raycasts.png -------------------------------------------------------------------------------- /docs/imgs/senzory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/senzory.png -------------------------------------------------------------------------------- /icons/warningicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/icons/warningicon.png -------------------------------------------------------------------------------- /networks/1610conti.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/networks/1610conti.h5 -------------------------------------------------------------------------------- /docs/dias/perceptron.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/dias/perceptron.dia -------------------------------------------------------------------------------- /docs/dias/perceptron.dia~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/dias/perceptron.dia~ -------------------------------------------------------------------------------- /docs/dias/supervised.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/dias/supervised.dia -------------------------------------------------------------------------------- /docs/dias/supervised.dia~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/dias/supervised.dia~ -------------------------------------------------------------------------------- /docs/imgs/logisticka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/logisticka.png -------------------------------------------------------------------------------- /docs/imgs/modelinfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/modelinfo.png -------------------------------------------------------------------------------- /docs/imgs/supervised.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/supervised.png -------------------------------------------------------------------------------- /networks/35themaster.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/networks/35themaster.h5 -------------------------------------------------------------------------------- /textures/carTexture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/textures/carTexture.png -------------------------------------------------------------------------------- /.vs/NeuralSandbox2/v16/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/.vs/NeuralSandbox2/v16/.suo -------------------------------------------------------------------------------- /docs/dias/futurereward.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/dias/futurereward.dia -------------------------------------------------------------------------------- /docs/dias/futurereward.dia~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/dias/futurereward.dia~ -------------------------------------------------------------------------------- /docs/dias/reinforcement.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/dias/reinforcement.dia -------------------------------------------------------------------------------- /docs/imgs/editaddpanels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/editaddpanels.png -------------------------------------------------------------------------------- /docs/imgs/editnetowork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/editnetowork.png -------------------------------------------------------------------------------- /docs/imgs/futurereward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/futurereward.png -------------------------------------------------------------------------------- /docs/imgs/leftrightdep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/leftrightdep.png -------------------------------------------------------------------------------- /docs/imgs/reinforcement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/reinforcement.png -------------------------------------------------------------------------------- /docs/scripts/Plots/model.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/scripts/Plots/model.h5 -------------------------------------------------------------------------------- /docs/imgs/K-means visual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/K-means visual.png -------------------------------------------------------------------------------- /docs/imgs/perceptron-dia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/perceptron-dia.png -------------------------------------------------------------------------------- /.vs/PythonSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "SuppressEnvironmentCreationPrompt": true, 3 | "Interpreter": "Workspace|Workspace|venv" 4 | } -------------------------------------------------------------------------------- /docs/dokumentace_projektu_cs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/dokumentace_projektu_cs.pdf -------------------------------------------------------------------------------- /docs/imgs/perceptron-sl-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/perceptron-sl-data.png -------------------------------------------------------------------------------- /docs/scripts/Plots/model_big.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/scripts/Plots/model_big.h5 -------------------------------------------------------------------------------- /networks/100contiMaster-35-3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/networks/100contiMaster-35-3.h5 -------------------------------------------------------------------------------- /networks/9layer255contir3-35.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/networks/9layer255contir3-35.h5 -------------------------------------------------------------------------------- /__pycache__/config.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/__pycache__/config.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/config.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/__pycache__/config.cpython-36.pyc -------------------------------------------------------------------------------- /ai/__pycache__/DQN.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/DQN.cpython-35.pyc -------------------------------------------------------------------------------- /ai/__pycache__/DQN.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/DQN.cpython-36.pyc -------------------------------------------------------------------------------- /ai/__pycache__/DQN.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/DQN.cpython-37.pyc -------------------------------------------------------------------------------- /ai/__pycache__/SGA.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/SGA.cpython-35.pyc -------------------------------------------------------------------------------- /ai/__pycache__/SGA.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/SGA.cpython-36.pyc -------------------------------------------------------------------------------- /ai/__pycache__/SGA.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/SGA.cpython-37.pyc -------------------------------------------------------------------------------- /ai/__pycache__/bank.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/bank.cpython-36.pyc -------------------------------------------------------------------------------- /ai/__pycache__/bank.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/bank.cpython-37.pyc -------------------------------------------------------------------------------- /ai/__pycache__/brain.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/brain.cpython-36.pyc -------------------------------------------------------------------------------- /docs/scripts/Plots/model_conti.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/scripts/Plots/model_conti.h5 -------------------------------------------------------------------------------- /objs/__pycache__/Car.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/Car.cpython-35.pyc -------------------------------------------------------------------------------- /objs/__pycache__/Car.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/Car.cpython-36.pyc -------------------------------------------------------------------------------- /objs/__pycache__/Car.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/Car.cpython-37.pyc -------------------------------------------------------------------------------- /__pycache__/NeuralApp.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/__pycache__/NeuralApp.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/NeuralApp.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/__pycache__/NeuralApp.cpython-36.pyc -------------------------------------------------------------------------------- /ai/__pycache__/Models.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/Models.cpython-37.pyc -------------------------------------------------------------------------------- /ai/__pycache__/SeqGen.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/SeqGen.cpython-35.pyc -------------------------------------------------------------------------------- /ai/__pycache__/SeqGen.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/SeqGen.cpython-36.pyc -------------------------------------------------------------------------------- /ai/__pycache__/SeqGen.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/SeqGen.cpython-37.pyc -------------------------------------------------------------------------------- /ai/__pycache__/models.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/models.cpython-35.pyc -------------------------------------------------------------------------------- /ai/__pycache__/models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/models.cpython-36.pyc -------------------------------------------------------------------------------- /docs/imgs/perceptron-sl-predicted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/docs/imgs/perceptron-sl-predicted.png -------------------------------------------------------------------------------- /objs/__pycache__/CarAI.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/CarAI.cpython-35.pyc -------------------------------------------------------------------------------- /objs/__pycache__/CarAI.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/CarAI.cpython-36.pyc -------------------------------------------------------------------------------- /objs/__pycache__/CarAI.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/CarAI.cpython-37.pyc -------------------------------------------------------------------------------- /objs/__pycache__/Barrier.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/Barrier.cpython-36.pyc -------------------------------------------------------------------------------- /objs/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /objs/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /objs/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /objs/__pycache__/kivyObjs.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/kivyObjs.cpython-35.pyc -------------------------------------------------------------------------------- /objs/__pycache__/kivyObjs.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/kivyObjs.cpython-36.pyc -------------------------------------------------------------------------------- /objs/__pycache__/kivyObjs.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/kivyObjs.cpython-37.pyc -------------------------------------------------------------------------------- /windows/__pycache__/Level.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/Level.cpython-36.pyc -------------------------------------------------------------------------------- /windows/__pycache__/Level.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/Level.cpython-37.pyc -------------------------------------------------------------------------------- /windows/__pycache__/PopNot.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/PopNot.cpython-35.pyc -------------------------------------------------------------------------------- /windows/__pycache__/PopNot.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/PopNot.cpython-36.pyc -------------------------------------------------------------------------------- /windows/__pycache__/PopNot.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/PopNot.cpython-37.pyc -------------------------------------------------------------------------------- /ai/__pycache__/GameController.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/GameController.cpython-35.pyc -------------------------------------------------------------------------------- /ai/__pycache__/GameController.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/GameController.cpython-36.pyc -------------------------------------------------------------------------------- /ai/__pycache__/GameController.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/GameController.cpython-37.pyc -------------------------------------------------------------------------------- /ai/__pycache__/TrainController.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/ai/__pycache__/TrainController.cpython-36.pyc -------------------------------------------------------------------------------- /objs/__pycache__/GameObjects.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/GameObjects.cpython-35.pyc -------------------------------------------------------------------------------- /objs/__pycache__/GameObjects.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/GameObjects.cpython-36.pyc -------------------------------------------------------------------------------- /objs/__pycache__/GameObjects.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/objs/__pycache__/GameObjects.cpython-37.pyc -------------------------------------------------------------------------------- /windows/__pycache__/MainWindow.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/MainWindow.cpython-36.pyc -------------------------------------------------------------------------------- /windows/__pycache__/Simulation.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/Simulation.cpython-35.pyc -------------------------------------------------------------------------------- /windows/__pycache__/Simulation.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/Simulation.cpython-36.pyc -------------------------------------------------------------------------------- /windows/__pycache__/Simulation.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/Simulation.cpython-37.pyc -------------------------------------------------------------------------------- /windows/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /windows/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /windows/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /windows/__pycache__/CanvasWindow.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/CanvasWindow.cpython-35.pyc -------------------------------------------------------------------------------- /windows/__pycache__/CanvasWindow.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/CanvasWindow.cpython-36.pyc -------------------------------------------------------------------------------- /windows/__pycache__/CanvasWindow.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/CanvasWindow.cpython-37.pyc -------------------------------------------------------------------------------- /windows/__pycache__/GameWidgets.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/GameWidgets.cpython-35.pyc -------------------------------------------------------------------------------- /windows/__pycache__/GameWidgets.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/GameWidgets.cpython-36.pyc -------------------------------------------------------------------------------- /windows/__pycache__/GameWidgets.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/GameWidgets.cpython-37.pyc -------------------------------------------------------------------------------- /windows/__pycache__/ImportExport.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/ImportExport.cpython-35.pyc -------------------------------------------------------------------------------- /windows/__pycache__/ImportExport.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/ImportExport.cpython-36.pyc -------------------------------------------------------------------------------- /windows/__pycache__/ImportExport.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/ImportExport.cpython-37.pyc -------------------------------------------------------------------------------- /windows/__pycache__/LayersWindow.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/LayersWindow.cpython-35.pyc -------------------------------------------------------------------------------- /windows/__pycache__/LayersWindow.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/LayersWindow.cpython-36.pyc -------------------------------------------------------------------------------- /windows/__pycache__/LayersWindow.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/LayersWindow.cpython-37.pyc -------------------------------------------------------------------------------- /windows/__pycache__/CanvasHandler.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/CanvasHandler.cpython-35.pyc -------------------------------------------------------------------------------- /windows/__pycache__/CanvasHandler.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/CanvasHandler.cpython-36.pyc -------------------------------------------------------------------------------- /windows/__pycache__/CanvasHandler.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisuskurka/NeuralQsandbox/HEAD/windows/__pycache__/CanvasHandler.cpython-37.pyc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | MainApplication/dist/ 2 | MainApplication/build/ 3 | MainApplication/__pycache__/ 4 | .vscode/ 5 | .vs/ 6 | venv/ 7 | build/ 8 | dist/ 9 | tmp/ 10 | 11 | -------------------------------------------------------------------------------- /docs/scripts/Plots/relu.py: -------------------------------------------------------------------------------- 1 | import matplotlib 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | 5 | # Data for plotting 6 | t = np.arange(-5.0, 5.0, 0.01) 7 | 8 | s = (t + abs(t)) / 2 9 | 10 | fig, ax = plt.subplots() 11 | ax.plot(t, s) 12 | 13 | ax.set(xlabel='x', ylabel='R(x)') 14 | ax.grid() 15 | 16 | plt.show() -------------------------------------------------------------------------------- /docs/scripts/Plots/logistic.py: -------------------------------------------------------------------------------- 1 | import matplotlib 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | 5 | # Data for plotting 6 | t = np.arange(-5.0, 5.0, 0.01) 7 | exp_t = np.exp(t) 8 | s = exp_t / (exp_t + 1) 9 | 10 | fig, ax = plt.subplots() 11 | ax.plot(t, s) 12 | 13 | ax.set(xlabel='x', ylabel='S(x)') 14 | ax.grid() 15 | 16 | plt.show() -------------------------------------------------------------------------------- /.vs/VSWorkspaceState.json: -------------------------------------------------------------------------------- 1 | { 2 | "ExpandedNodes": [ 3 | "", 4 | "\\MainApplication", 5 | "\\MainApplication\\ai", 6 | "\\MainApplication\\objs", 7 | "\\MainApplication\\templates", 8 | "\\MainApplication\\windows" 9 | ], 10 | "SelectedNode": "\\MainApplication\\windows\\GameWidgets.py", 11 | "PreviewInSolutionExplorer": false 12 | } -------------------------------------------------------------------------------- /docs/scripts/Plots/step.py: -------------------------------------------------------------------------------- 1 | import matplotlib 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | 5 | # Data for plotting 6 | t = np.arange(-5.0, 5.0, 0.01) 7 | s = [] 8 | 9 | for val in t: 10 | s.append(0 if(val < 0) else 1) 11 | 12 | fig, ax = plt.subplots() 13 | ax.plot(t, s) 14 | 15 | ax.set(xlabel='x', ylabel='B(x)') 16 | ax.grid() 17 | 18 | plt.show() -------------------------------------------------------------------------------- /ai/bank.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | 4 | """ 5 | Function bank for AI 6 | 7 | 8 | 9 | """ 10 | 11 | # Print packet data 12 | def printPackets(packets): 13 | for packet in packets: 14 | print("Score: {}, Data len: {}".format(packet["score"], len(packet["data"]))) 15 | 16 | # Unpack packet data 17 | def unpack(packets): 18 | observations = [] 19 | actions = [] 20 | for packet in packets: 21 | game_data = packet["data"] 22 | for data in game_data: 23 | observations.append(data[0]) 24 | actions.append(data[1]) 25 | 26 | return [observations, actions] -------------------------------------------------------------------------------- /ai/SeqGen.py: -------------------------------------------------------------------------------- 1 | from keras.utils import Sequence 2 | import numpy as np 3 | import time 4 | 5 | class SeqGen(Sequence): 6 | def __init__(self, x_set, y_set, batch_size): 7 | self.x, self.y = x_set, y_set 8 | self.batch_size = batch_size 9 | 10 | def __len__(self): 11 | return int(np.ceil(len(self.x) / float(self.batch_size))) 12 | 13 | def __getitem__(self, idx): 14 | # print("Fetching batch {}".format(idx)) 15 | time.sleep(0.5) 16 | 17 | batch_x = self.x[idx * self.batch_size:(idx + 1) * self.batch_size] 18 | batch_y = self.y[idx * self.batch_size:(idx + 1) * self.batch_size] 19 | 20 | return batch_x, batch_y -------------------------------------------------------------------------------- /neural.ini: -------------------------------------------------------------------------------- 1 | [Game] 2 | boolraycasts = 1 3 | numraycasts = 3 4 | angleraycasts = 35.0 5 | 6 | [AI] 7 | learn_type = DQN 8 | network_type = Sequential 9 | 10 | [DQN] 11 | dqn_discount_factor = 0.95 12 | dqn_exploration_max = 1.0 13 | dqn_exploration_min = 0.01 14 | dqn_exploration_decay = 0.995 15 | dqn_batch_size = 20 16 | dqn_experience_type = ER 17 | 18 | [SGA] 19 | sga_mutation_rate = 0.01 20 | sga_population_size = 30 21 | 22 | [NV] 23 | structure = [['dense', {'units': 64, 'activation': 'relu'}], ['dense', {'units': 128, 'activation': 'relu'}], ['dense', {'units': 64, 'activation': 'relu'}]] 24 | 25 | [Debug] 26 | boolraycasts = 1 27 | numraycasts = 3 28 | angleraycasts = 70 29 | 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behaviour: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behaviour** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Windows; Linux Ubuntu] 28 | - Version [e.g. 1.01] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /requirements_linux.txt: -------------------------------------------------------------------------------- 1 | absl-py==0.9.0 2 | altgraph==0.16.1 3 | astor==0.6.2 4 | certifi==2019.11.28 5 | cffi==1.13.2 6 | chardet==3.0.4 7 | docutils==0.15.2 8 | future==0.18.2 9 | gast==0.2.2 10 | google-pasta==0.1.8 11 | grpcio==1.26.0 12 | h5py==2.10.0 13 | idna==2.8 14 | Keras==2.2.5 15 | Keras-Applications==1.0.8 16 | Keras-Preprocessing==1.1.0 17 | Kivy==1.11.1 18 | Kivy-Garden==0.1.4 19 | kivy-garden.graph==0.4.0 20 | Markdown==3.1.1 21 | numpy==1.18.0 22 | opt-einsum==3.1.0 23 | pefile==2019.4.18 24 | protobuf==3.11.2 25 | pycparser==2.19 26 | Pygments==2.5.2 27 | pymunk==5.6.0 28 | PyYAML==5.2 29 | requests==2.22.0 30 | scipy==1.4.1 31 | six==1.13.0 32 | tensorboard==1.15.0 33 | tensorflow==1.15.0 34 | tensorflow-estimator==1.15.1 35 | termcolor==1.1.0 36 | urllib3==1.25.7 37 | Werkzeug==0.16.0 38 | wrapt==1.11.2 39 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | absl-py==0.9.0 2 | altgraph==0.16.1 3 | astor==0.6.2 4 | certifi==2019.11.28 5 | cffi==1.13.2 6 | chardet==3.0.4 7 | docutils==0.15.2 8 | future==0.18.2 9 | gast==0.2.2 10 | google-pasta==0.1.8 11 | grpcio==1.26.0 12 | h5py==2.10.0 13 | idna==2.8 14 | Keras==2.2.5 15 | Keras-Applications==1.0.8 16 | Keras-Preprocessing==1.1.0 17 | Kivy==1.11.1 18 | kivy-deps.glew==0.1.12 19 | kivy-deps.gstreamer==0.1.18 20 | kivy-deps.sdl2==0.1.23 21 | Kivy-Garden==0.1.4 22 | kivy-garden.graph==0.4.0 23 | Markdown==3.1.1 24 | numpy==1.18.0 25 | opt-einsum==3.1.0 26 | pefile==2019.4.18 27 | protobuf==3.11.2 28 | pycparser==2.19 29 | Pygments==2.5.2 30 | pymunk==5.6.0 31 | pypiwin32==223 32 | pywin32==227 33 | pywin32-ctypes==0.2.0 34 | PyYAML==5.2 35 | requests==2.22.0 36 | scipy==1.4.1 37 | six==1.13.0 38 | tensorboard==1.15.0 39 | tensorflow==1.15.0 40 | tensorflow-estimator==1.15.1 41 | termcolor==1.1.0 42 | urllib3==1.25.7 43 | Werkzeug==0.16.0 44 | wrapt==1.11.2 45 | -------------------------------------------------------------------------------- /templates/mainmenu.kv: -------------------------------------------------------------------------------- 1 | #:kivy 1.0 2 | 3 | : 4 | GridLayout: 5 | cols: 3 6 | canvas: 7 | Rectangle: 8 | pos: self.pos 9 | size: self.size 10 | source: 'pic.png' 11 | 12 | GridLayout: 13 | rows: 4 14 | padding: root.width/100 15 | spacing: root.height/50 16 | 17 | canvas: 18 | Color: 19 | rgba: 0, 0, 0, 0.6 20 | Rectangle: 21 | pos: self.pos 22 | size: self.size 23 | 24 | Button: 25 | text: "SandBox!" 26 | font_size: (root.width + root.height) / 100 27 | on_press: 28 | app.start_game() 29 | Button: 30 | text: "Options" 31 | font_size: (root.width + root.height) / 100 32 | on_press: 33 | app.open_settings() 34 | Button: 35 | text: "Exit" 36 | font_size: (root.width + root.height) / 100 37 | on_press: app.stop() 38 | Label: 39 | text: "" 40 | Label: 41 | text: "" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NeuralQsandbox 2 | 3 | 4 | NeuralQsandbox is an open-source environment for playing and experimenting with artificial intelligence.
5 |
6 | Create a level and watch agent with artificial brain try to solve it. Modify learning variables and restructure agent's brain, then watch agent's change in behaviour. 7 | 8 | ## Installation 9 | 10 | ### Windows 11 | Download latest release [here](https://github.com/Komarovec/NeuralQsandbox/releases) and simply run *NeuralQsandbox.exe* 12 | 13 | ### Linux 14 | Create a virtual environment with Python 3.5 (or newer), and with pip install all requirements specified in *requirements_linux.txt*. 15 | 16 | ```bash 17 | pip install -r requirements_linux.txt 18 | ``` 19 | 20 | This project also uses tkinter. If you don't have it installed already, you can install it by typing: 21 | 22 | ```bash 23 | sudo apt install python3-tk 24 | ``` 25 | 26 | ## Features 27 | - Custom levels with level editor 28 | - Exporting/Importing levels 29 | - Reinforcement learning (DQN) 30 | - Exporting/Importing models 31 | - Control many DQN variables 32 | 33 | ## Documentation 34 | Paper describing this project can be found here: 35 | https://github.com/Komarovec/NeuralQsandbox/blob/master/docs/dokumentace_projektu_cs.pdf 36 | -------------------------------------------------------------------------------- /ai/models.py: -------------------------------------------------------------------------------- 1 | # AI Framework 2 | from keras.models import Sequential 3 | from keras.layers import Dense 4 | from keras.optimizers import Adam 5 | 6 | import tensorflow as tf 7 | 8 | DEFAULT_STRUCTURE = [["dense", {"units":64, "activation":"relu"}],["dense", {"units":128, "activation":"relu"}],["dense", {"units":64, "activation":"relu"}]] 9 | 10 | # Creates sequential model 11 | def SequentialModel(input_size, output_size, learningRate, structure=None): 12 | print("creating new model") 13 | 14 | graph = tf.get_default_graph() 15 | 16 | # Default model 17 | with graph.as_default(): 18 | if(structure == None): 19 | structure = DEFAULT_STRUCTURE 20 | 21 | model = Sequential() 22 | for i, layer in enumerate(structure): 23 | if(layer[0] == "dense"): # Dense class 24 | if(i == 0): # Input layer 25 | model.add(Dense(layer[1]["units"], activation=layer[1]["activation"], input_dim=input_size)) 26 | else: # Hidden layers 27 | model.add(Dense(layer[1]["units"], activation=layer[1]["activation"])) 28 | model.add(Dense(output_size, activation="linear")) # Output layer 29 | 30 | # Optimizer 31 | adam = Adam(lr=learningRate) 32 | 33 | # Compile model 34 | model.compile(loss='mean_squared_error', optimizer=adam) 35 | model.summary() 36 | 37 | return model -------------------------------------------------------------------------------- /NeuralTester.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | from kivy_deps import sdl2, glew, gstreamer 3 | from PyInstaller.utils.hooks import collect_submodules, collect_data_files 4 | import os, pymunk, numpy 5 | 6 | pymunk_dir = os.path.dirname(pymunk.__file__) 7 | chipmunk_libs = [ 8 | ('chipmunk.dll', os.path.join(pymunk_dir, 'chipmunk.dll'), 'DATA'), 9 | ] 10 | 11 | block_cipher = None 12 | 13 | 14 | a = Analysis(['NeuralApp.py'], 15 | pathex=['D:\\Entertaiment\\Programy\\Python\\NeuralSandbox2'], 16 | binaries=None, 17 | datas=collect_data_files('tensorflow_core', subdir=None, include_py_files=True), 18 | hiddenimports=['h5py','h5py.defs','h5py.utils','h5py.h5ac','h5py._proxy'] + collect_submodules('tensorflow_core'), 19 | hookspath=[], 20 | runtime_hooks=[], 21 | excludes=[], 22 | win_no_prefer_redirects=False, 23 | win_private_assemblies=False, 24 | cipher=block_cipher 25 | ) 26 | pyz = PYZ(a.pure, a.zipped_data, 27 | cipher=block_cipher) 28 | 29 | exe = EXE(pyz, 30 | a.scripts, 31 | a.binaries, 32 | a.zipfiles, 33 | a.datas, 34 | name='NeuralQsandbox', 35 | debug=True, 36 | strip=False, 37 | upx=True, 38 | console=True ) 39 | 40 | coll = COLLECT(exe, 41 | a.binaries + chipmunk_libs, 42 | a.zipfiles, 43 | a.datas, 44 | *[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)], 45 | strip=False, 46 | upx=True, 47 | name='NeuralQsandbox') 48 | -------------------------------------------------------------------------------- /docs/scripts/Plots/choicePlot.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | from matplotlib import cm 3 | from mpl_toolkits.mplot3d import Axes3D 4 | import numpy as np 5 | from keras.models import load_model 6 | 7 | try: 8 | model = load_model("model_big.h5") 9 | except: 10 | print("Cannot load model!") 11 | model = None 12 | 13 | steps = 50 14 | 15 | def generateStates(): 16 | states = [] 17 | for i in range(0, 100, int(100/steps)): 18 | for j in range(0, 100, int(100/steps)): 19 | state = [] 20 | state.append(0.8) 21 | state.append(i/100) 22 | state.append(j/100) 23 | states.append(np.array([state])) 24 | 25 | return np.array(states) 26 | 27 | def calculateActions(states, actionSpace): 28 | actions = [] 29 | for i in range(0, steps): 30 | actionsInRow = [] 31 | for j in range(0, steps): 32 | actionsInRow.append(model.predict_on_batch(states[j+(i*steps)])[0][actionSpace]) 33 | 34 | actions.append(actionsInRow) 35 | 36 | return np.array(actions) 37 | 38 | def main(): 39 | # Generate state data 40 | states = generateStates() 41 | forward_prop = calculateActions(states, 0) 42 | left_prop = calculateActions(states, 1) 43 | right_prop = calculateActions(states, 2) 44 | 45 | sum_of_matrix = forward_prop + left_prop + right_prop 46 | 47 | left_prop_g = left_prop/sum_of_matrix 48 | right_prop_g = right_prop/sum_of_matrix 49 | forward_prop_g = forward_prop/sum_of_matrix 50 | 51 | X = np.arange(0, 1, 1/steps) 52 | Y = np.arange(0, 1, 1/steps) 53 | X, Y = np.meshgrid(X, Y) 54 | 55 | fig = plt.figure() 56 | ax = Axes3D(fig) 57 | 58 | #ax.plot_surface(X, Y, forward_prop_g, rstride=1, cstride=1, cmap=cm.cividis) 59 | ax.plot_surface(X, Y, left_prop_g, rstride=1, cstride=1, cmap=cm.viridis) 60 | ax.plot_surface(X, Y, right_prop_g, rstride=1, cstride=1, cmap=cm.inferno) 61 | 62 | plt.xlabel('Right sensor') 63 | plt.ylabel('Left sensor') 64 | 65 | plt.show() 66 | 67 | if __name__ == "__main__": 68 | if(model != None): 69 | main() -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | game_settings_json = json.dumps([ 4 | {"type": "title", 5 | "title": "Game settings"}, 6 | 7 | {"type": "bool", 8 | "title": "Draw raycasts", 9 | "section": "Game", 10 | "key": "boolraycasts"}, 11 | 12 | {"type": "raycasts", 13 | "title": "Number of raycasts. 1 - 20", 14 | "section": "Game", 15 | "key": "numraycasts"}, 16 | 17 | {"type": "angle", 18 | "title": "Angle of raycasts. 1 - 180", 19 | "section": "Game", 20 | "key": "angleraycasts"} 21 | ]) 22 | 23 | ai_settings_json = json.dumps([ 24 | {"type": "title", 25 | "title": "AI settings"}, 26 | {"type": "options", 27 | "title": "Learning type", 28 | "desc": "Change the way neural network is learned.", 29 | "section": "AI", 30 | "key": "learn_type", 31 | "options": ["DQN"]}, 32 | 33 | {"type": "options", 34 | "title": "Network type", 35 | "desc": "Change functional structure of neural network.", 36 | "section": "AI", 37 | "key": "network_type", 38 | "options": ["Sequential"]}, 39 | 40 | 41 | {"type": "title", 42 | "title": "DQN settings"}, 43 | {"type": "multiplier", 44 | "title": "Discount factor", 45 | "desc": "Discout of future estimated reward. <0,1>", 46 | "section": "DQN", 47 | "key": "dqn_discount_factor"}, 48 | 49 | {"type": "multiplier", 50 | "title": "Exploration max", 51 | "desc": "Starting exploration value. <0,1>", 52 | "section": "DQN", 53 | "key": "dqn_exploration_max"}, 54 | 55 | {"type": "multiplier", 56 | "title": "Exploration min", 57 | "desc": "Minimal exploration value. <0,1>", 58 | "section": "DQN", 59 | "key": "dqn_exploration_min"}, 60 | 61 | {"type": "multiplier", 62 | "title": "Exploration decay", 63 | "desc": "How much will exploration decrease over time. <0,1>", 64 | "section": "DQN", 65 | "key": "dqn_exploration_decay"}, 66 | 67 | {"type": "batch", 68 | "title": "Batch size", 69 | "desc": "Change the batch size of experience replay. 10 - 100", 70 | "section": "DQN", 71 | "key": "dqn_batch_size"}, 72 | 73 | {"type": "options", 74 | "title": "Experience replay model", 75 | "desc": "Change model of ER", 76 | "section": "DQN", 77 | "key": "dqn_experience_type", 78 | "options": ["ER"]}, 79 | ]) -------------------------------------------------------------------------------- /docs/scripts/PerceptronPointClassification/main.py: -------------------------------------------------------------------------------- 1 | 2 | import random 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | import tensorflow as tf 6 | from tensorflow import keras 7 | 8 | # Konstanty 9 | a = 1 10 | b = 0 11 | power = 1 12 | 13 | #G enerace dat 14 | def generateData(val): 15 | data = np.empty((0,2), int) 16 | for i in range(0, val): 17 | posX = random.random() 18 | posY = random.random() 19 | data = np.append(data, [[posX,posY]], axis=0) 20 | return data 21 | 22 | # Testovani dat 23 | def testData(data): 24 | label = np.empty((0,1), int) 25 | for i in data: 26 | if(i[1] < a*pow(i[0],power)+b): 27 | label = np.append(label, 0) 28 | else: 29 | label = np.append(label, 1) 30 | return label 31 | 32 | # Plotting 33 | def plotData(data, label): 34 | i = 0 35 | fig = plt.figure() 36 | ax1 = fig.add_subplot() 37 | ax1.set_ylabel('y') 38 | ax1.set_xlabel('x') 39 | for item in data: 40 | ax1.plot(item[0], item[1], 'o', color = (label[i],1-label[i],0.0)) 41 | i = i + 1 42 | plt.show() 43 | 44 | 45 | def main(*args): 46 | train_data = generateData(5000) 47 | train_label = testData(train_data) 48 | 49 | test_data = generateData(1000) 50 | test_label = testData(test_data) 51 | 52 | print("Data shape:") 53 | print("Data shape: "+str(train_data.shape)) 54 | print("Data: "+str(train_data)) 55 | print("Index: "+str(train_data[0,0])) 56 | print("----------------") 57 | 58 | plotData(test_data, test_label) 59 | 60 | #Single layer perceptron 61 | model = keras.Sequential([ 62 | keras.layers.Dense(16, input_shape=(2,)), 63 | keras.layers.Dropout(0.5), 64 | keras.layers.Dense(1, activation=tf.nn.sigmoid) 65 | ]) 66 | 67 | model.compile(optimizer='adam', 68 | loss='binary_crossentropy', 69 | metrics=['accuracy']) 70 | 71 | history = model.fit(train_data, 72 | train_label, 73 | batch_size=128, 74 | epochs=50) 75 | 76 | test_loss, test_acc = model.evaluate(test_data, test_label) 77 | print('Test accuracy:', test_acc) 78 | 79 | predictions = model.predict(test_data) 80 | print("Prediction: "+str(predictions[0].shape)) 81 | 82 | predictionsTest = [] 83 | for i in predictions: 84 | predictionsTest.append(i[0]) 85 | 86 | plotData(test_data, predictionsTest) 87 | 88 | if __name__ == "__main__": 89 | main() -------------------------------------------------------------------------------- /objs/Car.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | import pymunk 3 | import pymunk.autogeometry 4 | from pymunk.vec2d import Vec2d 5 | from kivy.graphics import Color, Quad 6 | 7 | # Custom functions and classes 8 | from objs.kivyObjs import points_from_poly 9 | 10 | class Car(pymunk.Poly): 11 | CAR = "car" 12 | 13 | def __init__(self, mass=10, size=(100,50), pos=(100,100), friction=1, ground_friction=0.9, angular_friction=0.9, forward_speed = 5000, backward_speed = 5000, angular_speed = 500, elasticity=0.4, rgba=(0.8,0,0,1), texture=None): 14 | body = pymunk.Body(mass, pymunk.moment_for_box(mass, size)) 15 | shape = pymunk.Poly.create_box(body, (size[0],size[1])) 16 | super(Car, self).__init__(shape._get_body(), shape.get_vertices()) 17 | 18 | self.objectType = self.CAR 19 | 20 | self.rgba = rgba 21 | self.forward_speed = forward_speed 22 | self.backward_speed = backward_speed 23 | self.angular_speed = angular_speed 24 | self.texture = texture 25 | 26 | self.ground_friction = ground_friction 27 | self.angular_friction = angular_friction 28 | self.size = size 29 | self.body.position = pos 30 | self.friction = friction 31 | self.elasticity = elasticity 32 | 33 | self.filter = pymunk.ShapeFilter(categories=1, mask=pymunk.ShapeFilter.ALL_MASKS ^ 1) 34 | 35 | # Respawn at the first spawnpoint in shapes array 36 | def respawn(self, simulation): 37 | self.body.position = simulation.findSpawnpoint() 38 | self.body.angle = 0 39 | self.body.velocity = (0,0) 40 | 41 | self.isDead = False 42 | 43 | # Paint Car 44 | def paint(self, canvasHandler): 45 | with canvasHandler.canvas: 46 | Color(rgba=self.rgba) 47 | 48 | # self.ky = Quad(points=points_from_poly(self, scaller), texture=self.texture) 49 | self.ky = Quad(points=points_from_poly(self, canvasHandler.scaller)) 50 | 51 | # Control methods 52 | def forward(self, vel): 53 | self.body.apply_force_at_local_point(Vec2d(self.forward_speed*vel,0), (0,0)) 54 | 55 | def backward(self, vel): 56 | self.body.apply_force_at_local_point(Vec2d(-self.backward_speed*vel,0), (0,0)) 57 | 58 | def left(self, vel): 59 | self.body.apply_force_at_local_point(Vec2d(-self.angular_speed*vel,0), (0,self.size[0])) 60 | self.body.apply_force_at_local_point(Vec2d(self.angular_speed*vel,0), (0,-self.size[0])) 61 | 62 | def right(self, vel): 63 | self.body.apply_force_at_local_point(Vec2d(self.angular_speed*vel,0), (0,self.size[0])) 64 | self.body.apply_force_at_local_point(Vec2d(-self.angular_speed*vel,0), (0,-self.size[0])) 65 | -------------------------------------------------------------------------------- /windows/PopNot.py: -------------------------------------------------------------------------------- 1 | from kivy.uix.popup import Popup 2 | from kivy.uix.label import Label 3 | from kivy.uix.button import Button 4 | from kivy.uix.boxlayout import BoxLayout 5 | from kivy.uix.image import Image 6 | 7 | WARNING_ICON = "warning" 8 | DANGER_ICON = "danger" 9 | INFO_ICON = "info" 10 | 11 | def getIcon(picture): 12 | if(picture == WARNING_ICON): 13 | return Image(source="icons/warningicon.png", size_hint=(0.4, 1)) 14 | elif(picture == DANGER_ICON): 15 | return Image(source="icons/dangericon.png", size_hint=(0.4, 1)) 16 | elif(picture == INFO_ICON): 17 | return Image(source="icons/infoicon.png", size_hint=(0.4, 1)) 18 | else: 19 | return None 20 | 21 | class InfoPopup(): 22 | def __init__(self, text, title="Information", picture=None, size=(400,300)): 23 | # Popup layouts 24 | content = BoxLayout(orientation="vertical") 25 | subcontent = BoxLayout(orientation="horizontal") 26 | 27 | # Create popup object 28 | popup = Popup(title=title, content=content, size_hint=(None,None), size=size) 29 | 30 | # Add text and icon 31 | subcontent.add_widget(Label(text=text)) 32 | icon = getIcon(picture) 33 | if(icon != None): 34 | subcontent.add_widget(icon) 35 | 36 | # Create popup 37 | content.add_widget(subcontent) 38 | content.add_widget(Button(text="Close", on_press=popup.dismiss, size_hint=(1, 0.2))) 39 | popup.open() 40 | 41 | class ConfirmPopup(): 42 | def __init__(self, text, title, fnc, picture=None, size=(400,300)): 43 | # Execute this function when clicked yes 44 | self.fnc = fnc 45 | 46 | # Popup layouts 47 | content = BoxLayout(orientation="vertical") 48 | subcontent = BoxLayout(orientation="horizontal") 49 | buttons = BoxLayout(orientation="horizontal", size_hint=(1, 0.2)) 50 | 51 | # Create popup object 52 | self.popup = popup = Popup(title=title, content=content, size_hint=(None,None), size=size) 53 | 54 | # Add text and icon 55 | subcontent.add_widget(Label(text=text)) 56 | icon = getIcon(picture) 57 | if(icon != None): 58 | subcontent.add_widget(icon) 59 | 60 | # When user wants to continue 61 | def clickedYes(*args): 62 | self.fnc() 63 | self.popup.dismiss() 64 | 65 | # Add buttons 66 | buttons.add_widget(Button(text="Cancel", on_press=popup.dismiss)) 67 | buttons.add_widget(Button(text="Continue", on_press=clickedYes)) 68 | 69 | # Create popup 70 | content.add_widget(subcontent) 71 | content.add_widget(buttons) 72 | popup.open() 73 | -------------------------------------------------------------------------------- /objs/GameObjects.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | import pymunk 3 | import pymunk.autogeometry 4 | from pymunk.vec2d import Vec2d 5 | from kivy.graphics import Color, Line, Ellipse 6 | from objs.kivyObjs import ellipse_from_circle, newRectangle 7 | 8 | class StaticGameObject(): 9 | BARRIER = "barrier" 10 | NOBARRIER = "nobarrier" 11 | START = "start" 12 | FINISH = "finish" 13 | 14 | def __init__(self, objectType, rgba=(0.2,0.2,0.2,1), friction=1, elasticity=0.85, texture=None): 15 | # Common attrs 16 | self.objectType = objectType 17 | self.rgba = rgba 18 | self.friction = friction 19 | self.elasticity = elasticity 20 | 21 | def createSegment(self, a, b, radius, canvasHandler): 22 | body = pymunk.Body(mass=0, moment=0, body_type=pymunk.Body.STATIC) 23 | self.shape = pymunk.Segment(body, a, b, radius) 24 | self.addAttrs(self.shape) 25 | self.paint(canvasHandler) 26 | canvasHandler.simulation.space.add(self.shape) 27 | 28 | def createCircle(self, pos, radius, canvasHandler): 29 | body = pymunk.Body(mass=0, moment=0, body_type=pymunk.Body.STATIC) 30 | body.position = pos 31 | self.shape = pymunk.Circle(body, radius) 32 | self.addAttrs(self.shape) 33 | self.paint(canvasHandler) 34 | canvasHandler.simulation.space.add(self.shape) 35 | 36 | def createBox(self, pos, size, canvasHandler): 37 | body = pymunk.Body(mass=0, moment=0, body_type=pymunk.Body.STATIC) 38 | body.position = pos 39 | self.shape = pymunk.Poly.create_box(body, (size[0],size[1])) 40 | self.addAttrs(self.shape) 41 | self.paint(canvasHandler) 42 | canvasHandler.simulation.space.add(self.shape) 43 | 44 | def createBoxPoints(self, points, canvasHandler): 45 | body = pymunk.Body(mass=0, moment=0, body_type=pymunk.Body.STATIC) 46 | self.shape = pymunk.Poly(body, points) 47 | self.addAttrs(self.shape) 48 | self.paint(canvasHandler) 49 | canvasHandler.simulation.space.add(self.shape) 50 | 51 | def addAttrs(self, shape): 52 | self.shape.friction = self.friction 53 | self.shape.elasticity = self.elasticity 54 | self.shape.rgba = self.rgba 55 | self.shape.objectType = self.objectType 56 | 57 | if(self.objectType == self.START or self.objectType == self.NOBARRIER): 58 | self.shape.sensor = True 59 | elif(self.objectType == self.FINISH): 60 | self.shape.filter = pymunk.ShapeFilter(categories=10) 61 | 62 | def paint(self, canvasHandler): 63 | with canvasHandler.canvas: 64 | Color(rgba=self.rgba) 65 | if(isinstance(self.shape, pymunk.Segment)): 66 | scalled_points = (self.shape.a[0]*canvasHandler.scaller,self.shape.a[1]*canvasHandler.scaller, 67 | self.shape.b[0]*canvasHandler.scaller,self.shape.b[1]*canvasHandler.scaller) 68 | self.shape.ky = Line(points=scalled_points, width=self.shape.radius*canvasHandler.scaller) 69 | elif(isinstance(self.shape, pymunk.Circle)): 70 | self.shape.ky = ellipse_from_circle(self.shape, canvasHandler.scaller) 71 | elif(isinstance(self.shape, pymunk.Poly)): 72 | self.shape.ky = newRectangle(self.shape, canvasHandler.scaller) -------------------------------------------------------------------------------- /objs/kivyObjs.py: -------------------------------------------------------------------------------- 1 | import kivy 2 | kivy.require('1.0.7') 3 | from kivy.app import App 4 | from kivy.lang import Builder 5 | from kivy.uix.widget import Widget 6 | from kivy.uix.screenmanager import ScreenManager, Screen 7 | from kivy.uix.label import Label 8 | from kivy.uix.floatlayout import FloatLayout 9 | from kivy.uix.scatter import Scatter 10 | from kivy.uix.scatterlayout import ScatterLayout 11 | from kivy.uix.relativelayout import RelativeLayout 12 | from kivy.uix.anchorlayout import AnchorLayout 13 | from kivy.uix.actionbar import ActionBar 14 | from kivy.uix.image import Image 15 | from kivy.clock import Clock 16 | from kivy.graphics import Ellipse, Line, Color, Triangle, Quad, Rectangle, Mesh 17 | from kivy.core.window import Window 18 | from kivy.core.image import Image as CoreImage 19 | 20 | import cffi 21 | import pymunk 22 | import pymunk.autogeometry 23 | from pymunk.vec2d import Vec2d 24 | 25 | import math 26 | import numpy as np 27 | 28 | # Kivy functions 29 | def paintObject(shape, canvasHandler): 30 | with canvasHandler.canvas: 31 | Color(rgba=shape.rgba) 32 | if(isinstance(shape, pymunk.Segment)): 33 | scalled_points = (shape.a[0]*canvasHandler.scaller,shape.a[1]*canvasHandler.scaller, 34 | shape.b[0]*canvasHandler.scaller,shape.b[1]*canvasHandler.scaller) 35 | shape.ky = Line(points=scalled_points, width=shape.radius*canvasHandler.scaller) 36 | elif(isinstance(shape, pymunk.Circle)): 37 | shape.ky = ellipse_from_circle(shape, canvasHandler.scaller) 38 | elif(isinstance(shape, pymunk.Poly)): 39 | shape.ky = newRectangle(shape, canvasHandler.scaller) 40 | 41 | def ellipse_from_circle(shape, scaller=1): 42 | body = shape.body 43 | pos = body.position - (shape.radius, shape.radius) 44 | e = Ellipse(pos=pos * (scaller, scaller), size=[shape.radius*2*scaller, shape.radius*2*scaller]) 45 | return e 46 | 47 | def points_from_poly(shape, scaller=1): 48 | body = shape.body 49 | ps = [p.rotated(body.angle) + body.position for p in shape.get_vertices()] 50 | vs = [] 51 | for p in ps: 52 | vs += [p.x * scaller, p.y * scaller] 53 | return vs 54 | 55 | def newRectangle(shape, scaller=1): 56 | return Quad(points=points_from_poly(shape, scaller)) 57 | 58 | 59 | # Math functions 60 | def centerPoint(a, b): 61 | return ((a[0]+b[0])/2,(a[1]+b[1])/2) 62 | 63 | def distXY(a, b): 64 | return math.sqrt(((a[0]-b[0])**2)+((a[1]-b[1])**2)) 65 | 66 | def getVector(a, b): 67 | return (b[0]-a[0], b[1]-a[1]) 68 | 69 | def normalizeVector(v): 70 | norm = np.linalg.norm(v) 71 | if(norm == 0): 72 | return v 73 | 74 | return v / norm 75 | 76 | def calculateRectangle(a, b, c): 77 | if(distXY(a,b) <= 0 or distXY(a,c) <= 0 or distXY(b,c) <= 0): 78 | return (None,None,None,None) 79 | 80 | # Calculate remaining points for Rectangle !!!MATH WARNING!!! 81 | alpha = math.atan2(c[1]-a[1], c[0]-a[0]) - math.atan2(b[1]-a[1], b[0]-a[0]) 82 | distAT = distXY(a, c) * math.cos(alpha) 83 | t = distAT/distXY(a,b) 84 | vectAT = ((b[0] - a[0])*t, (b[1] - a[1])*t) 85 | 86 | T = (a[0]+vectAT[0],a[1]+vectAT[1]) 87 | vectTC = (c[0]-T[0], c[1]-T[1]) 88 | 89 | Cdash = (a[0]+vectTC[0], a[1]+vectTC[1]) 90 | D = (b[0]+vectTC[0], b[1]+vectTC[1]) 91 | # END OF !!!MATH WARNING!!! 92 | 93 | # Rename 94 | c = Cdash 95 | d = D 96 | return (a,b,c,d) 97 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at komarovec1[AT]gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /windows/CanvasWindow.py: -------------------------------------------------------------------------------- 1 | import kivy 2 | from kivy.app import App 3 | from kivy.uix.screenmanager import Screen 4 | from kivy.animation import Animation 5 | 6 | # Custom classes 7 | from windows.GameWidgets import ToolBar, ToolBarGame, ToolBarEditor, StateBar, StateInfoBar, StartMenu, ObjectMenu, EditMenu 8 | from windows.Simulation import Simulation 9 | from windows.CanvasHandler import CanvasHandler 10 | from objs.GameObjects import StaticGameObject 11 | 12 | # Window class, manages GUI and creates internal CanvasHandler object 13 | class CanvasWindow(Screen): 14 | def __init__(self, **kwargs): 15 | super().__init__(**kwargs) 16 | self.game = None 17 | self.app = App.get_running_app() 18 | 19 | # Entered the screen 20 | def on_enter(self): 21 | if(self.game == None): 22 | self.create_game() 23 | 24 | # Screen left 25 | def on_leave(self): 26 | pass 27 | 28 | # Deltes CanvasHandler object and all GUI 29 | def delete_game(self): 30 | self.game.stop() 31 | self.clear_widgets() 32 | self.remove_widget(self.game) 33 | self.game = None 34 | 35 | # Creates CanvasHandler object and all GUI 36 | def create_game(self): 37 | self.game = CanvasHandler() 38 | self.gameToolbar = ToolBarGame(self.manager, self.game, self) 39 | self.editorToolbar = ToolBarEditor(self.manager, self.game, self) 40 | self.statebar = StateBar(self.game, self) 41 | self.stateInfoBar = StateInfoBar(self.game, self) 42 | self.objectMenu = ObjectMenu(self.game, self) 43 | self.editMenu = EditMenu(self.game, self) 44 | self.startMenu = StartMenu(self.game, self) 45 | 46 | self.game.size_hint = 1,1 47 | self.game.pos = 0,0 48 | 49 | self.add_widget(self.game, 10) 50 | self.add_widget(self.statebar) 51 | self.add_widget(self.gameToolbar) 52 | 53 | self.simulation = Simulation(self.game) 54 | 55 | self.game.init(self) 56 | self.game.start(self.simulation) 57 | 58 | self.toggleStartMenu(True) 59 | # self.add_widget(self.build()) 60 | 61 | # Open/Close start menu // True/False 62 | def toggleStartMenu(self, state): 63 | if(state): 64 | self.add_widget(self.startMenu) 65 | else: 66 | self.remove_widget(self.startMenu) 67 | 68 | # Open/Close start menu 69 | def toggleObjectMenu(self): 70 | if(self.objectMenu.visible): 71 | self.disableObjectMenu() 72 | else: 73 | self.enableObjectMenu() 74 | 75 | # Closes object menu 76 | def disableObjectMenu(self): 77 | self.remove_widget(self.objectMenu) 78 | self.objectMenu.visible = False 79 | 80 | # Opens object menu 81 | def enableObjectMenu(self): 82 | size = self.objectMenu.ids["mainLayout"].size[1] 83 | self.objectMenu.ids["mainLayout"].size[1] = 0 84 | 85 | self.add_widget(self.objectMenu) 86 | self.objectMenu.visible = True 87 | 88 | animation = Animation(size=(self.objectMenu.ids["mainLayout"].size[0],size), duration=.1) 89 | animation.start(self.objectMenu.ids["mainLayout"]) 90 | 91 | # Closes/Opens object menu 92 | def toggleStateInfoBar(self, state): 93 | if(state == "normal"): 94 | self.remove_widget(self.stateInfoBar) 95 | else: 96 | self.add_widget(self.stateInfoBar) 97 | 98 | # Ends level editor & closes everything 99 | def endLevelEditor(self): 100 | self.remove_widget(self.editorToolbar) 101 | self.add_widget(self.gameToolbar) 102 | self.disableObjectMenu() 103 | 104 | self.game.changeState("game") 105 | 106 | # OPens level editor & opens everything 107 | def startLevelEditor(self): 108 | self.add_widget(self.editorToolbar) 109 | self.remove_widget(self.gameToolbar) 110 | self.enableObjectMenu() 111 | 112 | self.game.changeState("editor") 113 | -------------------------------------------------------------------------------- /ai/SGA.py: -------------------------------------------------------------------------------- 1 | from objs.CarAI import CarAI 2 | from ai.models import SequentialModel 3 | 4 | import numpy as np 5 | 6 | class SGA(): 7 | def __init__(self, pop_size=10, mutation_rate=0.1, population=[]): 8 | # Max reward 9 | self.highestReward = 0 10 | self.highestRewardedModel = None 11 | 12 | # Avarage reward 13 | self.averageReward = 0 14 | 15 | # Basic variables 16 | self.pop_size = pop_size 17 | self.mutation_rate = mutation_rate 18 | self.generation = 0 19 | 20 | self.population = population 21 | 22 | # Generates random population 23 | def randomPopulation(self, simulation): 24 | self.generation = 0 25 | 26 | self.population = [] 27 | for _ in range(self.pop_size): 28 | self.population.append(simulation.addCarAI()) 29 | 30 | # sort population by fitness 31 | def sortPopulation(self): 32 | self.population.sort(key=lambda x: x.reward, reverse=True) 33 | 34 | # Select best individuals from population 35 | def selectParents(self, hm_parents=2): 36 | parents = [] 37 | 38 | # Sort population by fitness 39 | self.sortPopulation() 40 | 41 | # Pick n parents by best fitness 42 | for i in range(hm_parents): 43 | parents.append(self.population[i]) 44 | 45 | return parents 46 | 47 | # Calculate average fitness 48 | def averageFitness(self): 49 | fitness_sum = 0 50 | for individual in self.population: 51 | fitness_sum += individual.reward 52 | 53 | fitness_average = fitness_sum / self.pop_size 54 | self.averageReward = fitness_average 55 | 56 | return fitness_average 57 | 58 | # Crossover 59 | def crossover(self, parents): 60 | for car in self.population: 61 | print("Crossover for {}".format(car)) 62 | model = car.model 63 | 64 | # Iterate through layers 65 | for k, layer in enumerate(model.layers): 66 | new_weights_for_layer = [] 67 | # Each layer has 2 matrizes, one for connection weights and one for biases 68 | # Then itterate through each matrix 69 | 70 | for j, weight_array in enumerate(layer.get_weights()): 71 | # Save their shape 72 | save_shape = weight_array.shape 73 | # Reshape them to one dimension 74 | one_dim_weight = weight_array.reshape(-1) 75 | 76 | # Get weights from all parents 77 | parentsWeights = [] 78 | for parent in parents: 79 | pWeights = parent.model.layers[k].get_weights()[j] 80 | pWeights = pWeights.reshape(-1) 81 | parentsWeights.append(pWeights) 82 | 83 | for i, _ in enumerate(one_dim_weight): 84 | # Go through individual weights 85 | # Pick random parent and set weight 86 | parentIndex = np.random.randint(len(parents)) 87 | one_dim_weight[i] = parentsWeights[parentIndex][i] 88 | 89 | # Random mutation 90 | one_dim_weight[i] = self.mutateWeight(one_dim_weight[i], self.mutation_rate) 91 | 92 | # Reshape them back to the original form 93 | new_weight_array = one_dim_weight.reshape(save_shape) 94 | 95 | # Save them to the weight list for the layer 96 | new_weights_for_layer.append(new_weight_array) 97 | 98 | # Set the new weight list for each layer 99 | model.layers[k].set_weights(new_weights_for_layer) 100 | 101 | # Mutation 102 | def mutateWeight(self, weight, mutation_rate): 103 | if(mutation_rate <= np.random.random()): 104 | return np.random.normal(loc=0,scale=1) 105 | else: 106 | return weight 107 | 108 | # Calculate fitness of a model 109 | def calculateFitness(self, car, simulation): 110 | dist = car.distToFinish(simulation) 111 | 112 | # When no finish found 113 | if(dist == None): 114 | fitness = 0 115 | 116 | # Calculate fitness based on distance to finish 117 | elif(dist != 0): 118 | fitness = fitness = 1/dist 119 | 120 | # If distance is 0 --> finish found 121 | else: 122 | fitness = fitness = 100000 123 | print("Solution found!") 124 | 125 | return fitness*10000 126 | 127 | # Create new population 128 | def newPopulation(self, simulation): 129 | # Calculate average reward 130 | self.averageFitness() 131 | 132 | # Selection --> select the best 133 | parents = self.selectParents() 134 | 135 | # Save the best model and his reward 136 | highestReward = parents[0].reward 137 | highestRewardedModel = parents[0] 138 | 139 | # Count only the best of the best 140 | if(self.highestReward < highestReward): 141 | self.highestReward = highestReward 142 | self.highestRewardedModel = highestRewardedModel 143 | 144 | # Make new population 145 | with simulation.graph.as_default(): 146 | self.crossover(parents) 147 | 148 | # Respawn population 149 | for car in self.population: 150 | car.respawn(simulation) 151 | 152 | # Increment generation number 153 | self.generation += 1 154 | 155 | # Car died --> calculate fitness 156 | def carDied(self, car, simulation): 157 | car.reward = self.calculateFitness(car, simulation) 158 | 159 | # Check cars 160 | def isDone(self, simulation): 161 | done = True 162 | 163 | # Episode continues 164 | for car in self.population: 165 | if(not car.isDead): 166 | car.think(rc=car.calculateRaycasts(simulation.space), graph=simulation.graph) 167 | done = False 168 | 169 | return done 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /ai/DQN.py: -------------------------------------------------------------------------------- 1 | import keras 2 | import random 3 | import math 4 | import numpy as np 5 | import multiprocessing as mp 6 | from collections import deque 7 | 8 | # Sequence generator 9 | from ai.SeqGen import SeqGen 10 | from objs.kivyObjs import distXY 11 | from windows.PopNot import InfoPopup 12 | import windows.PopNot as PN 13 | 14 | class DQN(): 15 | def __init__(self, discount=0.95, exploration_min=0.01, exploration_max=1, exploration_decay=0.995, batch_size=20): 16 | # Set constants 17 | self.discount = discount 18 | self.exploration_min = exploration_min 19 | self.exploration_max = exploration_max 20 | self.exploration_decay = exploration_decay 21 | self.batch_size = batch_size 22 | 23 | # Prepare variables 24 | self.highestReward = 0 25 | self.highestRewardedModel = None 26 | self.deathCount = 0 27 | self.tempSAPair = None 28 | self.exploration_rate = None # Create it just you know it exists :) 29 | self.resetExplorationRate() 30 | 31 | # Variable for storing state-action pairs 32 | self.memory = deque() 33 | self.hm_steps = 0 34 | 35 | # Learning object 36 | self.dqnCar = None 37 | 38 | # Reset exploration rate 39 | def resetExplorationRate(self): 40 | self.exploration_rate = self.exploration_max 41 | 42 | # Decreases exploration rate by decay 43 | def decayExplorationRate(self): 44 | self.exploration_rate *= self.exploration_decay 45 | self.exploration_rate = max(self.exploration_min, self.exploration_rate) 46 | 47 | # Respawn object 48 | def respawnCar(self, simulation): 49 | # Save best rewarded (reward + model) 50 | if(self.dqnCar != None): 51 | self.deathCount += 1 52 | if(self.dqnCar.reward > self.highestReward): 53 | self.highestReward = self.dqnCar.reward 54 | self.highestRewardedModel = self.dqnCar.model 55 | 56 | # Reset level & steps 57 | simulation.resetLevel() 58 | self.startSteps = simulation.space.steps 59 | 60 | # If respawn save brain then load it again 61 | if(self.dqnCar != None): 62 | self.dqnCar = simulation.addCarAI(self.dqnCar.model) 63 | # First spawn --> create brain 64 | else: 65 | if(simulation.gameController.exportModel != None): 66 | self.dqnCar = simulation.addCarAI(simulation.gameController.exportModel) 67 | else: 68 | self.dqnCar = simulation.addCarAI() 69 | 70 | # Test car 71 | if(self.dqnCar == None): return 72 | 73 | # Set camera 74 | simulation.canvasWindow.selectedCar = self.dqnCar 75 | 76 | # Reset reward when new spawn 77 | self.dqnCar.reward = 0 78 | 79 | # Choose action based in observation or explore 80 | def act(self, model, obs, graph=None, action_space=2): 81 | if(np.random.random() < self.exploration_rate): 82 | # Take random action 83 | return np.random.randint(action_space) 84 | 85 | else: 86 | # Predict action from AI model 87 | with graph.as_default(): 88 | return np.argmax(model.predict(obs)[0]) # Return only highest predicted index 89 | 90 | # Save state-action pair 91 | def remember(self, obs, action, obs1, reward): 92 | self.memory.append((obs, action, obs1, reward)) 93 | 94 | # Half Q-Learning 95 | def fast_experience_replay(self, model, graph): 96 | if(len(self.memory) < self.batch_size): 97 | return 98 | 99 | # Select random memories 100 | batch = random.sample(self.memory, self.batch_size) 101 | obsToLearn = [] 102 | actionsToLearn = [] 103 | 104 | for obs, action, obs1, reward in batch: 105 | # Q Function : Immediate reward + Future reward 106 | with graph.as_default(): 107 | q_update = reward + self.discount * np.amax(model.predict(obs1)[0]) 108 | 109 | # Predict on current value 110 | q_values = model.predict(obs) # Predict overhead? -> Faster? 111 | 112 | # Update actual Q-Value 113 | q_values[0][action] = q_update 114 | 115 | # Save Q-Values for fitting 116 | obsToLearn.append(obs[0]) 117 | actionsToLearn.append(q_values[0]) 118 | 119 | obsToLearn = np.array(obsToLearn) 120 | actionsToLearn = np.array(actionsToLearn) 121 | 122 | # Fit on calculated Q-Values 123 | with graph.as_default(): 124 | model.fit(obsToLearn, actionsToLearn, verbose=0) 125 | 126 | # Decrease exploration rate 127 | self.decayExplorationRate() 128 | 129 | # Calculate immediate reward 130 | def immediateReward(self, observations): 131 | reward = 0 132 | 133 | # Reward if fast 134 | pos0 = self.pos0 135 | pos1 = self.dqnCar.body.position 136 | self.pos0 = pos1 137 | vel = distXY(pos0, pos1) 138 | if(vel >= 12 and vel < 20): 139 | reward = 2 140 | 141 | # Punish if close to the wall 142 | for observation in observations[0]: 143 | if(observation < 0.1): 144 | reward = -1/(observation*100) 145 | 146 | # Punish if died 147 | if(self.dqnCar.isDead): 148 | reward = -10 149 | 150 | return reward 151 | 152 | # Calls every memory step 153 | def step(self, simulation): 154 | # Take observation 155 | obs1 = self.dqnCar.calculateRaycasts(simulation.space) 156 | action1 = self.act(self.dqnCar.model, obs1, graph=simulation.graph, action_space=self.dqnCar.action_space) 157 | 158 | self.lastAction = action1 159 | self.dqnCar.takeAction(action1) 160 | 161 | # First time-step --> Save obs & action and use them in next time-step 162 | if(self.tempSAPair == None): 163 | self.tempSAPair = (obs1, action1) 164 | self.pos0 = self.dqnCar.body.position 165 | 166 | # Load prev state-actions 167 | obs = self.tempSAPair[0] 168 | action = self.tempSAPair[1] 169 | 170 | # Add reward to overall reward 171 | reward = self.immediateReward(obs) 172 | self.dqnCar.reward += reward 173 | 174 | # Remember state-action pairs 175 | self.remember(obs, action, obs1, reward) 176 | 177 | # Experience replay 178 | self.fast_experience_replay(self.dqnCar.model, graph=simulation.graph) 179 | 180 | # Replace old observation with new observation 181 | self.tempSAPair = (obs1, action1) -------------------------------------------------------------------------------- /remarks: -------------------------------------------------------------------------------- 1 | 4.7.2019 - 4h - Kivy ? 2 | 11.7.2019 - 4h - Kivy okna 3 | 12.7.2019 - 5h - Kivy víc oken + Pymunk 4 | 15.7.2019 - 3h - Pymunk implementace 5 | 16.7.2019 - 9h - Fyzika Pymunk, Škálování kanvasu 6 | 17.7.2019 - 8h - Škálování všech objektu, top-down fyzika (odpor), pohyb po kanvasu 7 | 18.7.2019 - 3h - Vylepšení grafiky, toolbar 8 | 20.7.2019 - 6h - Dědičnost tříd z Pymunku, Základy NS 9 | 22.7.2019 - 3h - Input klavesnice 10 | 23.7.2019 - 4h - Kolize, Lepší ovládání 11 | 24.7.2019 - 3h - Kolize, Arbiter, Škálování pomocí myš. kolečka, Základy level editoru (GUI, Barikády) 12 | 25.7.2019 - 9h - Start a Finish class, Kolizní masky, Level editor (pohyb Auta, Segmentu) 13 | 26.7.2019 - 9h - Level editor, zvyraznění těles, pohyb segmentu (celých), úprava kódu, test textur, build do exe 14 | 29.7.2019 - 6h - Rozdeleni hlavnich trid, zlepseni prehlednosti kodu, stavovy bar, serializace objektu (zacatek) 15 | 30.7.2019 - 8h - Serializace pomocí pickle, a nakonec ukladani do XMLyY 16 | 31.7.2019 - 9h - Kompletní přepsání statických herních objektů za účelem jednodušího kódu a větší modulárnosti 17 | 1.8.2019 - 11h - Uživatelské rozhraní, přídávání objektu pomocí ObjectMenu, vyber barev pomoci popupu a ColorPickeru. 18 | 2.8.2019 - 9h - přidavaní boxu, Pickle binární export a import levelu, grafické vytvarení boxu, oprava bugů 19 | 12.8.2019 - 4h - Oprava bugů, centrovaní kamery, fokus na dialog, alpha canal na vyberu barvy 20 | 13.8.2019 - 2h - Undo akce 21 | 14.8.2019 - 4h - GUI Rework, Oprava kolizí a kolizního handleru 22 | 15.8.2019 - 9h - Editovací menu, editování objektu (kolize, barva, typ), nový toolbar system, object menu předělan --> tool menu maztaní atd.. 23 | 16.8.2019 - 7h - Vrstvovací system, meneni vrstev v edit menu, zlepsene edit menu, chyba handeleru pri exportu/importu se vyskytuje pouze v game --> zakaz, zacatek popup, notif 24 | 19.8.2019 - 6h - Tensorflow opakovaní modelu (jiny projekt), lehke upravy 25 | 20.8.2019 - 7h - Příprava na AI --> Třida CarAI, měření vzdálenosti (obecná definice, vytváření sensorů), mat. funkce přesunuty, male upravy delete funkce... 26 | 21.8.2019 - 4h - AI --> dodělání měření vzdálenosti (raycast), 80+ objektu canvas <20 fps.. hledání řešení, první sekv. model 27 | 22-23.8.2019 - 5H - Tutoriály a dokumentace učení Tensorflow, TFLearn, atd.. 28 | 24.8.2019 - 3h - Diagram učení, základní implementace 29 | 25.8.2019 - 4h - Zlepšení diagramu, třída TrainController, učící smyčka 30 | 26.8.2019 - 2h - Učení s učitelem, funkce pro vypočet fitness zatím jen 1/vzdálenost k cili, nedokončene 31 | 27.8.2019 - 1h - Učení s učitelem 32 | 28.8.2019 - 2h - První funkčí učící smyčka! Velmi neefektiví, pomalá a hnusná... ale funkční :) 33 | 29.8.2019 - 2h - TrainController vylepšení třídy, lehké grafické upravy 34 | 30.8.2019 - 3h - TrainController učení, smyčka, problemy s fyzikou 35 | 31.8.2019 - 4h - TrainController učení, problemy s fyzikou vyřešeny! --> dt 36 | 1.9.2019 - 1h - Male vylepšení testovaní modelu 37 | 2.9.2019 - 4h - Měnění vlastnosti kamery (sledovaní aut, volný pohyb), Úkládání aut před vstupem do editoru --> nasledné nahrání 38 | 3.9.2019 - 1h - Trelo nastenka, mensi graficke upravy + upravy v repositari 39 | 4.9.2019 - 3h - Začátek přechodu z TFLearn na Keras 40 | 5.9.2019 - 2h - Keras, reimplementace funkcí 41 | 6.9.2019 - 4h - Keras --> funkční, cíle (trelo, github), Brain class přepsána na NeuralModel 42 | 7.9.2019 - 4h - Testování modelu, TrainController přejmenován na GameController a funkčnost byla pozměněna na kontrolu celé hry, základy StartMenu 43 | 8.9.2019 - 2h - Základy Uživatelského rozhraní (StartMenu) 44 | 9.9.2019 - 1h - Pokračování v UI 45 | 10.9.2019 - 4h - Základy herního módu pro hřáče --> Volný průjezd mapou --> Spojení s novým UI, Oprava bugů (Raycast Auta už se sám smaže) 46 | 11.9.2019 - 2h - Oprava bugů, GUI pro AI 47 | 12.9.2019 - 2h - AI 48 | 15.9.2019 - 4h - Cartpole OpenAI, testy AI 49 | 16.9.2019 - 4h - implementace poznatku z Cartpolu... Lehce funkčí stále ale naprosto nepouzitelne 50 | 17.9.2019 - 2h - Funkce pro náhodný přepis vah v síti --> mutace 51 | 18.9.2019 - 5h - GUI --> Detaily o učení, okno, vykreslování grafu --> fitness 52 | 19.9.2019 - 4h - Víc grafů, lepší zpracování informačního okna, vytvarení a mazaní okna uziv vstupem, nalezen novy bug (Extremni zaseky pri delsím ucení) 53 | 20.9.2019 - 10h - Q-Learning a jeho implementace s Neuronovou síti a pouze s tabulkou (DQN) 54 | 22.9.2019 - 6h - OpenAI Gym --> Q-Learning DQN 55 | 23.9.2019 - 8h - Překonání MountainCar challenge s pomocí DQN, Experience Learning (TFMiniProjects) --> začátky implementace DQN do hlavního projektu, Úpravy učícího algoritmu a optimalizace všech věcí kolem AI 56 | 24.9.2019 - 5h - Dokončení implementace DQN --> možné vylepšení (lepší okamžitá reward funkce) 57 | 25.9.2019 - 4h - Zlepšení grafů, Oprava follow kamery, optimalizace GameControlleru a učicí smyčky, oprava dalších mini-bugů (Kivy update unschedule --> cancel, ...) 58 | 26.9.2019 - 4h - Studium algoritmu NEAT, Zrychlení učení DQN 59 | 29.9.2019 - 2h - Návrh grafiky, Testování nastavení 60 | 30.9.2019 - 4h - nastavení menu, JSON nastavovací panely, ukladani nn modelu, zatim pouze struktura take JSON 61 | 1.10.2019 - 6h - Export/Import NeuralModelu, optimalizace NeuralModel --> nefunkci nechcese ucit DQN memory?, Overeni spravneho cteni a nacitani vseho. 62 | 2.10.2019 - 5h - Studium NEAT dokumentace 63 | 3.10.2019 - 7h - Reimplamentace experience replaye (late ver), zacatky paralelizace /multiprocessing a asychroniho uceni 64 | 4.10.2019 - 3h - Příprava pro separaci DQN 65 | 5.10.2019 - 10h - Separace DQN, přípravy pro implemetaci SGA, Zobecnění GameControlleru 66 | 8.10.2019 - 6h - Jednoduchý genetický algorytmus (SGA), Populace, selekce, mutace, dědičnost... 67 | 9.10.2019 - 6h - Popupy - Informační, potvrzující (pro nebezpečné akce) a hlašení výjimek. 68 | 12.10.2019 - 10h - GUI: Posílaní korektních informací pro různe učicí algoritmy, Informační hodnoty ve StateInfoBaru (U grafů) nyní funkční, Dokočení implementace SGA do GameControlleru 69 | 15.10.2019 - 2h - Začátek dokumentace 70 | 16.10.2019 - 5h - Kontrola Uživatelského vstupu (nastavení), připojení nastavení (funkční nastaveni DQN, SGA, ...), test nastavení 71 | 31.10.2019 - 6h - Threading, paralelizace --> lehka implementace (nový branch, zatím nefunkční) 72 | 1.11.2019 - 4h - Threading, reimplementace fyziky, učení DQN, Remote TF Graphy 73 | 2.11.2019 - 4h - Threading, SGA --> nefunkční 74 | 3.11.2019 - 6h - Threading, SGA opraveno a paralelizováno, crossover stále velmi pomalý (multiprocessing?), stále hodně chyb (vypnití a zapnuti testu...) 75 | 6.11.2019 - 3h - Threading, oprava bugů a extrémních situací 76 | 7.11.2019 - 6h - Threading implementován., Grafy konečně ukazují správné hodnoty pořád, Více možností v nastavení (Uhel a počet raycastů) 77 | 15.11.2019 - 5h - Raycasty nyni funguji mnohem lepe a jsou optimalizovane /Kivy best practices/ :), DQN Memory step implementovany i do test módu 78 | 16.11.2019 - 10h - Novy raycast system je nyni plne implementovany, Raycasty se nyni daji vypinat pres GUI, Vypinani PhysicsThreadu pri manipulaci se soubory 79 | 17.11.2019 - 6h - Variabilni simulacni rychlost, oprava odporu raycastů (nerealisticke zataceni) 80 | 23.11.2019 - 13h - Vytváření ns pomocí struktur + GUI pro návrh neurální sítě 81 | 24.11.2019 -> 1.1.2020 - 130h - Vytváření dokumentace (U no belif? https://i.gyazo.com/9f73a0528bedb96ff41ecafc3e0a801e.png) -------------------------------------------------------------------------------- /windows/LayersWindow.py: -------------------------------------------------------------------------------- 1 | import kivy 2 | from kivy.app import App 3 | from kivy.core.window import Window 4 | from kivy.graphics import Color, Rectangle 5 | from kivy.properties import ObjectProperty, NumericProperty 6 | from kivy.uix.widget import Widget 7 | from kivy.uix.screenmanager import ScreenManager, Screen 8 | from kivy.uix.gridlayout import GridLayout 9 | from kivy.uix.boxlayout import BoxLayout 10 | from kivy.uix.popup import Popup 11 | from kivy.uix.colorpicker import ColorPicker 12 | from kivy.uix.image import Image 13 | from kivy.uix.behaviors import ToggleButtonBehavior 14 | from kivy.uix.button import Button 15 | from kivy.uix.label import Label 16 | from kivy.uix.dropdown import DropDown 17 | from kivy.uix.scrollview import ScrollView 18 | from kivy.uix.textinput import TextInput 19 | from windows.GameWidgets import BGLabel, ActivationDropDown 20 | 21 | from functools import partial 22 | import re 23 | 24 | from ai.models import DEFAULT_STRUCTURE 25 | 26 | class ControlsWidget(BoxLayout): 27 | def __init__(self, addfun=None, resetfun=None, applyfun=None, **kwargs): 28 | super().__init__(orientation="vertical", size=(150,100), size_hint=(None, 1), padding=10, 29 | spacing=10, **kwargs) 30 | 31 | self.app = App.get_running_app() 32 | self.build() 33 | 34 | self.cancelBtn.bind(on_press=self.app.exit_layers) 35 | self.bindFun(addfun, resetfun, applyfun) 36 | 37 | self.bind(pos=self.draw) 38 | self.bind(size=self.draw) 39 | self.draw() 40 | 41 | def build(self): 42 | self.layerBtn = Button(text="Add layer", size_hint=(1, None), size=(50,50)) 43 | self.resetBtn = Button(text="Reset", size_hint=(1, None), size=(50,50)) 44 | self.cancelBtn = Button(text="Cancel", size_hint=(1, None), size=(50,50)) 45 | self.applyBtn = Button(text="Apply", size_hint=(1, None), size=(50,50)) 46 | 47 | self.add_widget(self.layerBtn) 48 | self.add_widget(self.resetBtn) 49 | self.add_widget(Label(text="", size_hint=(1, 1))) # Blank space 50 | self.add_widget(self.cancelBtn) 51 | self.add_widget(self.applyBtn) 52 | 53 | def draw(self, *args): 54 | if(self.canvas == None): return 55 | self.canvas.before.clear() 56 | with self.canvas.before: 57 | Color(rgba=(.15,.15,.15,1)) 58 | Rectangle(pos=self.pos, size=self.size) 59 | 60 | def bindFun(self, addfun=None, resetfun=None, applyfun=None): 61 | if(addfun != None): 62 | self.layerBtn.bind(on_press=addfun) 63 | if(resetfun != None): 64 | self.resetBtn.bind(on_press=resetfun) 65 | if(applyfun != None): 66 | self.applyBtn.bind(on_press=applyfun) 67 | 68 | class DenseLayerWidget(BoxLayout): 69 | def __init__(self, num, remfun, units=128, activation="relu", **kwargs): 70 | super().__init__(orientation="horizontal", padding=10,spacing=10, size=(100,50), size_hint=(1, None), **kwargs) 71 | self.remfun = remfun 72 | 73 | # Default values 74 | self.units = units 75 | self.activation = activation 76 | 77 | # Regex 78 | self.pat = re.compile('^[0-9]*$') 79 | 80 | self.unitInput = TextInput(text=str(self.units)) 81 | self.unitInput.bind(text=self.units_change) 82 | self.actiInput = Button(text=activation) 83 | self.dropdown = ActivationDropDown(func=self.activation_change) 84 | self.actiInput.bind(on_release=self.dropdown.open) 85 | self.dropdown.bind(on_select=lambda instance, x: setattr(self.actiInput, 'text', x)) 86 | 87 | self.removeBtn = Button(text="Remove", size_hint=(None, 1), size=(100,100)) 88 | self.removeBtn.bind(on_press=partial(remfun, self)) 89 | 90 | self.numLabel = Label(text=str(num)) 91 | 92 | self.add_widget(self.numLabel) 93 | self.add_widget(Label(text="Units: ")) 94 | self.add_widget(self.unitInput) 95 | self.add_widget(Label(text="Activation: ")) 96 | self.add_widget(self.actiInput) 97 | if(num == 0): 98 | self.add_widget(Label(text="Input", size_hint=(None, 1), size=(100,100))) 99 | else: 100 | self.add_widget(self.removeBtn) 101 | 102 | self.bind(pos=self.draw) 103 | self.bind(size=self.draw) 104 | self.draw() 105 | 106 | def draw(self, *args): 107 | if(self.canvas == None): return 108 | self.canvas.before.clear() 109 | with self.canvas.before: 110 | Color(rgba=(.15,.15,.15,1)) 111 | Rectangle(pos=self.pos, size=self.size) 112 | 113 | def num_change(self, num): 114 | self.numLabel.text = str(num) 115 | 116 | def activation_change(self, val): 117 | self.activation = val 118 | 119 | def units_change(self, obj, val, *_): 120 | if(self.pat.match(val) and val != ""): 121 | self.units = val 122 | else: 123 | self.unitInput.text = str(self.units) 124 | print(val) 125 | 126 | def remove(self): 127 | self.canvas.before.clear() 128 | 129 | class LayersWidget(Widget): 130 | def __init__(self, **kwargs): 131 | super().__init__(**kwargs) 132 | #(orientation="vertical", padding=10,spacing=10, **kwargs) 133 | self.app = App.get_running_app() 134 | 135 | self.allLayers = [] 136 | 137 | self.layout = BoxLayout(orientation="vertical", size_hint_y=None, padding=10, spacing=10) 138 | self.layout.bind(minimum_height=self.layout.setter('height')) 139 | 140 | self.sv = ScrollView(size_hint=(1, None), size=self.size, pos=self.pos) 141 | self.sv.add_widget(self.layout) 142 | self.add_widget(self.sv) 143 | 144 | def save_structure(self, *_): 145 | structure = [] 146 | for layer in self.allLayers: 147 | structure.append(["dense", {"units":int(layer.units), "activation":layer.activation}]) 148 | 149 | self.app.nstructure = structure 150 | 151 | def load_structure(self, *_): 152 | self.remove_all_layers() 153 | structure = self.app.nstructure 154 | if(structure == None): 155 | structure = DEFAULT_STRUCTURE 156 | 157 | for i, layer in enumerate(structure): 158 | if(layer[0] == "dense"): # Dense class 159 | self.add_layer(units=layer[1]["units"], activation=layer[1]["activation"]) 160 | 161 | def on_size(self, *_): 162 | if(self.canvas == None): return 163 | 164 | self.sv.size = self.size 165 | self.sv.pos = self.pos 166 | 167 | def add_layer(self, *_, units=128, activation="relu"): 168 | newLayer = DenseLayerWidget(self.allLayers.__len__(), remfun=self.remove_layer, units=units, activation=activation) 169 | self.layout.add_widget(newLayer) 170 | self.allLayers.append(newLayer) 171 | 172 | def remove_all_layers(self): 173 | for layer in self.allLayers: 174 | self.layout.remove_widget(layer) 175 | 176 | self.allLayers = [] 177 | 178 | def remove_layer(self, obj, *_): 179 | self.allLayers.remove(obj) 180 | self.layout.remove_widget(obj) 181 | 182 | newList = [] 183 | for i, layer in enumerate(self.allLayers): 184 | layer.num_change(i) 185 | newList.append(layer) 186 | self.allLayers = newList 187 | 188 | class LayersWindow(Screen): 189 | def __init__(self, **kwargs): 190 | super().__init__(**kwargs) 191 | 192 | self.mainLayout = None 193 | self.controls = ControlsWidget() 194 | self.layers = LayersWidget() 195 | 196 | self.controls.bindFun(addfun=self.layers.add_layer, 197 | resetfun=self.layers.load_structure, 198 | applyfun=self.layers.save_structure) 199 | 200 | # Entered the screen 201 | def on_enter(self): 202 | if(self.mainLayout == None): 203 | self.mainLayout = BoxLayout(orientation='horizontal') 204 | self.mainLayout.add_widget(self.controls) 205 | self.mainLayout.add_widget(self.createLayers()) 206 | self.add_widget(self.mainLayout) 207 | self.layers.load_structure() 208 | 209 | def createLayers(self): 210 | layersLayout = BoxLayout(orientation='vertical') 211 | layersLayout.add_widget(BGLabel(text="Layers", size_hint=(1, None), size=(50,50))) 212 | layersLayout.add_widget(self.layers) 213 | return layersLayout 214 | 215 | 216 | 217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /NeuralApp.py: -------------------------------------------------------------------------------- 1 | """ 2 | Basic menu system for NS2 3 | 4 | Denis Kurka - 11.7.2019 5 | """ 6 | 7 | import kivy 8 | kivy.require('1.0.7') 9 | from kivy.app import App 10 | from kivy.lang import Builder 11 | from kivy.uix.widget import Widget 12 | from kivy.uix.screenmanager import ScreenManager, Screen 13 | from kivy.uix.settings import SettingsWithSidebar 14 | from kivy.uix.settings import Settings 15 | from kivy.uix.settings import SettingString 16 | from kivy.uix.settings import InterfaceWithNoMenu 17 | from kivy.config import ConfigParser 18 | 19 | from windows.CanvasWindow import CanvasWindow 20 | from windows.LayersWindow import LayersWindow 21 | from windows.PopNot import ConfirmPopup, InfoPopup 22 | import windows.PopNot as PN 23 | from config import ai_settings_json, game_settings_json 24 | 25 | from keras.layers import Dense 26 | 27 | from kivy.config import Config 28 | Config.set('graphics', 'width', '1280') 29 | Config.set('graphics', 'height', '720') 30 | Config.set('input', 'mouse', 'mouse,multitouch_on_demand') 31 | Config.write() 32 | 33 | import numpy as np 34 | 35 | # Window classes 36 | class MainMenuWindow(Screen): 37 | pass 38 | 39 | # KV files import 40 | Builder.load_file("templates/mainmenu.kv") 41 | Builder.load_file("templates/canvas.kv") 42 | 43 | # Settings classes 44 | class ValidatedSettingsInterface(SettingsWithSidebar): 45 | pass 46 | 47 | class ValidatedSettings(Settings): 48 | def __init__(self, **kwargs): 49 | super(ValidatedSettings, self).__init__(**kwargs) 50 | self.register_type('multiplier', MultiplierValue) 51 | self.register_type('angle', AngleValue) 52 | self.register_type('raycasts', RaycastsValue) 53 | self.register_type('population', PopulationValue) 54 | self.register_type('batch', BatchValue) 55 | 56 | def add_kivy_panel(self): 57 | pass 58 | 59 | """ 60 | Custom datatypes 61 | """ 62 | # Validate percentage multipliers <0,1> 63 | class MultiplierValue(SettingString): 64 | def __init__(self, **kwargs): 65 | super(MultiplierValue, self).__init__(**kwargs) 66 | 67 | def _validate(self, instance): 68 | self._dismiss() # closes the popup 69 | 70 | # Clamp the value between <0,1> 71 | try: 72 | val = float(self.textinput.text) 73 | val = np.clip(val, 0, 1) 74 | self.value = str(val) 75 | except: 76 | return 77 | 78 | # Validate angles 79 | class AngleValue(SettingString): 80 | def __init__(self, **kwargs): 81 | super(AngleValue, self).__init__(**kwargs) 82 | 83 | def _validate(self, instance): 84 | self._dismiss() # closes the popup 85 | 86 | # Clamp the value between <0,1> 87 | try: 88 | val = float(self.textinput.text) 89 | val = np.clip(val, 1, 180) 90 | self.value = str(val) 91 | except: 92 | return 93 | 94 | # Validate raycasts 95 | class RaycastsValue(SettingString): 96 | def __init__(self, **kwargs): 97 | super(RaycastsValue, self).__init__(**kwargs) 98 | 99 | def _validate(self, instance): 100 | self._dismiss() # closes the popup 101 | 102 | # Clamp the value between <0,1> 103 | try: 104 | val = int(round(float(self.textinput.text))) 105 | val = np.clip(val, 1, 20) 106 | self.value = str(val) 107 | except: 108 | return 109 | 110 | # Validate batch size DQN 111 | class BatchValue(SettingString): 112 | def __init__(self, **kwargs): 113 | super(BatchValue, self).__init__(**kwargs) 114 | 115 | def _validate(self, instance): 116 | self._dismiss() # closes the popup 117 | 118 | # Clamp the value between <0,1> 119 | try: 120 | val = int(round(float(self.textinput.text))) 121 | val = np.clip(val, 10, 100) 122 | self.value = str(val) 123 | except: 124 | return 125 | 126 | # Validate population size SGA 127 | class PopulationValue(SettingString): 128 | def __init__(self, **kwargs): 129 | super(PopulationValue, self).__init__(**kwargs) 130 | 131 | def _validate(self, instance): 132 | self._dismiss() # closes the popup 133 | 134 | # Clamp the value between <0,1> 135 | try: 136 | val = int(round(float(self.textinput.text))) 137 | val = np.clip(val, 8, 50) 138 | self.value = str(val) 139 | except: 140 | return 141 | 142 | # Main class 143 | class NeuralApp(App): 144 | def __init__(self, **kwargs): 145 | super().__init__(**kwargs) 146 | 147 | # Internal var for tranfering neural structure between GUI and Game --> LayersWindow x CarAI x models 148 | self.nstructure = None 149 | 150 | # Screen manager 151 | self.sm = ScreenManager() 152 | self.mainMenu = MainMenuWindow(name="mainmenu") 153 | self.canvasWindow = CanvasWindow(name="canvas") 154 | self.layersWindow = LayersWindow(name="layers") 155 | 156 | self.sm.add_widget(self.mainMenu) 157 | self.sm.add_widget(self.canvasWindow) 158 | self.sm.add_widget(self.layersWindow) 159 | 160 | self.sm.current = "mainmenu" 161 | 162 | def build(self): 163 | self.settings_cls = ValidatedSettings 164 | return self.sm 165 | 166 | def build_config(self, config): 167 | config.setdefaults("Game", { 168 | "boolraycasts": False, 169 | "numraycasts": 3, 170 | "angleraycasts": 35 171 | }) 172 | 173 | config.setdefaults("AI", { 174 | "learn_type": "DQN", 175 | "network_type": "Sequential", 176 | }) 177 | 178 | config.setdefaults("DQN", { 179 | "dqn_discount_factor": 0.95, 180 | "dqn_exploration_max": 1, 181 | "dqn_exploration_min": 0.01, 182 | "dqn_exploration_decay": 0.995, 183 | "dqn_batch_size": 20, 184 | "dqn_experience_type": "ER", 185 | }) 186 | 187 | config.setdefaults("SGA", { 188 | "sga_mutation_rate": 0.01, 189 | "sga_population_size": 30, 190 | }) 191 | 192 | def build_settings(self, settings): 193 | settings.add_json_panel("AI panel", self.config, data=ai_settings_json) 194 | settings.add_json_panel("Game panel", self.config, data=game_settings_json) 195 | 196 | def on_config_change(self, config, section, key, value): 197 | pass 198 | 199 | # Config reset confirmed 200 | def config_reset_confirmed(self): 201 | config = self.config 202 | 203 | config.setdefaults("NV", { 204 | "structure": [["dense", {"units":64, "activation":"relu"}],["dense", {"units":128, "activation":"relu"}],["dense", {"units":64, "activation":"relu"}]], 205 | }) 206 | 207 | config.setall("Game", { 208 | "boolraycasts": False, 209 | "numraycasts": 3, 210 | "angleraycasts": 35 211 | }) 212 | 213 | config.setall("AI", { 214 | "learn_type": "DQN", 215 | "network_type": "Sequential", 216 | }) 217 | 218 | config.setall("DQN", { 219 | "dqn_discount_factor": 0.95, 220 | "dqn_exploration_max": 1, 221 | "dqn_exploration_min": 0.01, 222 | "dqn_exploration_decay": 0.995, 223 | "dqn_batch_size": 20, 224 | "dqn_experience_type": "ER", 225 | }) 226 | 227 | config.write() 228 | 229 | self.stop() 230 | 231 | # Reset of config 232 | def config_reset(self): 233 | ConfirmPopup("Reset will result in application restart!\n Do you really wish to continue?", 234 | "Config reset", self.config_reset_confirmed, PN.DANGER_ICON) 235 | 236 | # On config change 237 | def on_config_change(self, config, section, key, value): 238 | if config is self.config: 239 | token = (section, key) 240 | if token == ('Game', 'boolraycasts'): 241 | self.canvasWindow.game.raycastsVisibility((int(value) != 0)) 242 | 243 | # Layers screen 244 | def start_layers(self): 245 | self.sm.transition.direction = 'down' 246 | self.sm.current = 'layers' 247 | 248 | # Layers screen 249 | def exit_layers(self, *_): 250 | self.sm.transition.direction = 'up' 251 | self.sm.current = 'canvas' 252 | 253 | # Start sandbox 254 | def start_game(self): 255 | self.sm.transition.direction = 'left' 256 | self.sm.current = 'canvas' 257 | 258 | # Exit sandbox to mainmenu 259 | def exit_game(self): 260 | self.sm.transition.direction = 'right' 261 | self.sm.current = 'mainmenu' 262 | 263 | 264 | if __name__ == '__main__': 265 | NeuralApp().run() -------------------------------------------------------------------------------- /objs/CarAI.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | import pymunk 3 | import pymunk.autogeometry 4 | from pymunk.vec2d import Vec2d 5 | from kivy.graphics import Color, Quad, Line 6 | from kivy.app import App 7 | import math 8 | import numpy as np 9 | from statistics import median, mean 10 | from collections import Counter 11 | import random 12 | 13 | # AI Framework 14 | from keras.models import Sequential 15 | import keras 16 | 17 | # Custom functions and classes 18 | from objs.Car import Car 19 | from objs.kivyObjs import points_from_poly, centerPoint, getVector, normalizeVector, distXY 20 | 21 | from ai.models import SequentialModel 22 | 23 | class CarAI(Car): 24 | CARAI = "carai" 25 | 26 | def __init__(self, mass=10, size=(100,50), pos=(100,100), friction=1, ground_friction=0.9, angular_friction=0.9, forward_speed = 5000, 27 | backward_speed = 5000, angular_speed = 500, elasticity=0.4, rgba=(0.8,0,0,1), texture=None, model=None, learningRate=0.001): 28 | super(CarAI, self).__init__(mass, size, pos, friction, ground_friction, angular_friction, forward_speed, 29 | backward_speed, angular_speed, elasticity, rgba, texture) 30 | 31 | app = App.get_running_app() 32 | config = app.config 33 | 34 | self.objectType = self.CARAI 35 | self.raycastLenght = 2000 36 | self.raycastAngle = np.radians(float(config.get('Game','angleraycasts'))) 37 | self.raycastCount = int(config.get('Game','numraycasts'))-1 38 | self.raycastObjects = [] 39 | self.raycastColor = (1,1,1, int(config.get('Game', 'boolraycasts'))) 40 | 41 | self.action_space = 3 42 | 43 | # AI 44 | self.learningRate = learningRate 45 | self.isDead = False 46 | self.reward = 0 47 | 48 | # Speed 49 | self.speed = 10 50 | 51 | # LastAction 52 | self.lastAction = None 53 | 54 | # Create sequential model 55 | if(model == None): 56 | self.model = SequentialModel(self.raycastCount+1, self.action_space, self.learningRate, structure=app.nstructure) 57 | else: 58 | self.model = model 59 | self.raycastCount = self.model.layers[0].input_shape[1]-1 60 | 61 | # Create raycasts objects 62 | def createRaycasts(self, simulation): 63 | space = simulation.space 64 | points = points_from_poly(self) 65 | 66 | # Calculate raycasts 67 | a = centerPoint((points[0], points[1]), (points[6], points[7])) 68 | b = centerPoint((points[4], points[5]), (points[2], points[3])) 69 | 70 | # Calulate origin for vector calc 71 | origin = (0,0) 72 | 73 | # Normalize vector and prepare it for multiplication 74 | vectAB = normalizeVector(getVector(a, b)) 75 | 76 | # Main vector 77 | queryVectors = [] 78 | queryVectors.append(vectAB) 79 | 80 | # Calculate other vectors 81 | for i in range(self.raycastCount): 82 | angle = self.raycastAngle*(i+1) 83 | multiplier = 1 84 | # Move vector right to left in circle sectors 85 | if(i % 2 != 0): 86 | multiplier = -1 87 | angle -= self.raycastAngle 88 | 89 | # Sine and Cosine of the angle 90 | c, s = np.cos(angle*multiplier), np.sin(angle*multiplier) 91 | 92 | # Angle rotation --> Rotation matrix 93 | x, y = queryVectors[0] 94 | vectX = x * c - y * s 95 | vectY = x * s + y * c 96 | 97 | queryVectors.append((vectX, vectY)) 98 | 99 | 100 | # Check collisions of all vectors 101 | self.raycastObjects = [] 102 | for vect in queryVectors: 103 | # Calculate second point with direction vector 104 | b = (vect[0]*self.raycastLenght+origin[0],vect[1]*self.raycastLenght+origin[1]) 105 | segment = pymunk.Segment(None, (0,0), b, 5) 106 | segment.sensor = True 107 | segment.filter = pymunk.ShapeFilter(mask=pymunk.ShapeFilter.ALL_MASKS ^ 1 ^ 10) 108 | segment.rgba = self.raycastColor 109 | segment.raycast = True 110 | segment.mass = 0 111 | segment.density = 0 112 | segment.body = self.body 113 | segment.lastContact = segment.body.local_to_world(b) 114 | self.raycastObjects.append(segment) 115 | space.add(segment) 116 | 117 | self.drawRaycasts(simulation.canvasWindow) 118 | 119 | # Calculate distance for every raycast 120 | def calculateRaycasts(self, space): 121 | dist = [] 122 | 123 | # No raycasts? Return only zeros 124 | if(self.raycastObjects == []): 125 | for _ in range(self.raycastCount): 126 | dist.append(0) 127 | return np.array([dist]) 128 | 129 | # Calculate intersects if raycasts exist 130 | for raycast in self.raycastObjects: 131 | # Test query --> collisions 132 | query = space.segment_query_first(raycast.body.local_to_world(raycast.a), raycast.body.local_to_world(raycast.b), raycast.radius, pymunk.ShapeFilter(mask=pymunk.ShapeFilter.ALL_MASKS ^ 1 ^ 10)) 133 | # If collisions happened 134 | if(query): 135 | # Get point of contant with collidible object 136 | contact_point = query.point 137 | 138 | # Change graphics representation /Line/ 139 | raycast.lastContact = contact_point 140 | 141 | # Calculate distance to the collidible object based on segment query 142 | dist.append(distXY(raycast.body.local_to_world(raycast.a), contact_point)/self.raycastLenght) 143 | else: 144 | dist.append(1) 145 | 146 | # Change graphics representation /Line/ 147 | raycast.lastContact = raycast.body.local_to_world(raycast.b) 148 | 149 | return np.array([dist]) 150 | 151 | # Visibility of raycasts --> Requires kivy repaint! 152 | def raycastsVisibility(self, visibility): 153 | color = (1,1,1,int(visibility)) 154 | 155 | self.raycastColor = color 156 | for raycast in self.raycastObjects: 157 | raycast.rgba = color 158 | 159 | # Draw raycasts 160 | def drawRaycasts(self, canvasHandler): 161 | for raycast in self.raycastObjects: 162 | with canvasHandler.canvas: 163 | Color(raycast.rgba) 164 | scalled_points = (raycast.a[0]*canvasHandler.scaller,raycast.a[1]*canvasHandler.scaller, 165 | raycast.b[0]*canvasHandler.scaller,raycast.b[1]*canvasHandler.scaller) 166 | raycast.ky = Line(points=scalled_points, width=raycast.radius*canvasHandler.scaller) 167 | 168 | # Delete raycasts from canvas 169 | def deleteRaycasts(self, simulation): 170 | for raycast in self.raycastObjects: 171 | simulation.space.remove(raycast) 172 | simulation.canvasWindow.canvas.remove(raycast.ky) 173 | 174 | 175 | # Load raycasts 176 | def loadRaycasts(self, simulation): 177 | for raycast in self.raycastObjects: 178 | simulation.space.add(raycast) 179 | simulation.canvasWindow.canvas.add(Color(rgba=raycast.rgba)) 180 | simulation.canvasWindow.canvas.add(raycast.ky) 181 | 182 | # Calculate distance to nearest finish 183 | def distToFinish(self, simulation): 184 | point = simulation.findNearestFinish(self.body.position) 185 | 186 | if(point != None): 187 | return distXY(point, self.body.position) 188 | else: 189 | return None 190 | 191 | # Kill the car 192 | def kill(self, canvasHandler): 193 | self.isDead = True 194 | 195 | # Create new NN 196 | def generateRandomBrain(self): 197 | self.model = SequentialModel(self.raycastCount+1, self.action_space, self.learningRate) 198 | 199 | # Generate random decision 200 | def randomDecision(self): 201 | decision = [] 202 | for _ in range(2): 203 | decision.append(np.random.random()) 204 | 205 | return decision 206 | 207 | # Take last action 208 | def takeLastAction(self): 209 | if(self.lastAction != None): 210 | pass 211 | # self.takeAction(self.lastAction) 212 | 213 | # Makes car think 214 | def takeAction(self, action=None, dist=None, graph=None): 215 | # If no action provided, predict here 216 | if(action == None): 217 | # Predict action based on rays 218 | with graph.as_default(): 219 | results = self.model.predict(dist) 220 | 221 | action = np.argmax(results[0]) 222 | 223 | # Take appropriate action 224 | if(action == 0): 225 | self.forward(2) 226 | elif(action == 1): 227 | self.left(1) 228 | self.forward(2) 229 | elif(action == 2): 230 | self.right(1) 231 | self.forward(2) 232 | 233 | self.lastAction = action 234 | return action -------------------------------------------------------------------------------- /windows/ImportExport.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | import pymunk 3 | from objs.GameObjects import StaticGameObject 4 | from objs.Car import Car 5 | from objs.CarAI import CarAI 6 | from objs.kivyObjs import paintObject 7 | 8 | import tkinter as tk 9 | from tkinter import filedialog 10 | 11 | from keras.models import load_model 12 | 13 | from windows.PopNot import InfoPopup 14 | import windows.PopNot as PN 15 | 16 | import tensorflow as tf 17 | 18 | import pickle 19 | import sys, os 20 | 21 | class IENetwork(): 22 | NETWORK_TYPES = [("Hierarchical Data Format (*.h5)","*.h5")] 23 | 24 | @classmethod 25 | def exportNetwork(self, model, simulation): 26 | # Check if model exists 27 | if(model == None): 28 | InfoPopup("No model loaded!") 29 | return 30 | 31 | # Create filedialog 32 | root = tk.Tk() 33 | root.withdraw() 34 | 35 | pathname = os.path.abspath(os.path.dirname(sys.argv[0]))+"/networks" 36 | if not os.path.exists(pathname): 37 | os.makedirs(pathname) 38 | 39 | file_path = filedialog.asksaveasfilename(initialdir = pathname, title = "Save neural network", defaultextension=".h5", filetypes=self.NETWORK_TYPES) 40 | 41 | if(file_path != ""): 42 | simulation.canvasWindow.stopDrawing() 43 | simulation.endPhysicsThread() 44 | 45 | # Try exporting model 46 | try: 47 | model.save(file_path) 48 | # If exception occured print something 49 | except: 50 | InfoPopup("Something went wrong!", "Network export", PN.DANGER_ICON) 51 | # If everything went ok 52 | else: 53 | InfoPopup("Neural network\nsuccessfully exported!", "Network export", PN.INFO_ICON) 54 | 55 | simulation.startPhysicsThread() 56 | simulation.canvasWindow.startDrawing() 57 | 58 | @classmethod 59 | def importNetwork(self, simulation): 60 | # Create filedialog 61 | root = tk.Tk() 62 | root.withdraw() 63 | 64 | pathname = os.path.abspath(os.path.dirname(sys.argv[0]))+"/networks" 65 | if not os.path.exists(pathname): 66 | os.makedirs(pathname) 67 | 68 | file_path = filedialog.askopenfilename(initialdir = pathname, title = "Load neural network", defaultextension=".h5", filetypes=self.NETWORK_TYPES) 69 | 70 | if(file_path != ""): 71 | simulation.canvasWindow.stopDrawing() 72 | simulation.endPhysicsThread() 73 | 74 | model = None 75 | # Try importing model 76 | try: 77 | graph = tf.get_default_graph() 78 | with graph.as_default(): 79 | model = load_model(file_path) 80 | # If exception occured print something 81 | except: 82 | InfoPopup("Something went wrong!", "Network import", PN.DANGER_ICON) 83 | # If everything went ok 84 | else: 85 | InfoPopup("Neural network\nsuccessfully imported!", "Network import", PN.INFO_ICON) 86 | 87 | simulation.startPhysicsThread() 88 | simulation.canvasWindow.startDrawing() 89 | return model 90 | 91 | 92 | class IELevel(): 93 | LEVEL_TYPES = [("Pickled level (*.lvl)","*.lvl")] 94 | 95 | @classmethod 96 | def exportLevel(self, simulation): 97 | pathname = os.path.abspath(os.path.dirname(sys.argv[0]))+"/levels" 98 | if not os.path.exists(pathname): 99 | os.makedirs(pathname) 100 | 101 | # Create filedialog 102 | root = tk.Tk() 103 | root.withdraw() 104 | 105 | file_path = filedialog.asksaveasfilename(initialdir = pathname,title = "Save level", defaultextension=".lvl", filetypes=self.LEVEL_TYPES) 106 | 107 | if(file_path != ""): 108 | simulation.canvasWindow.stopDrawing() 109 | simulation.endPhysicsThread() 110 | 111 | cars = [] 112 | space = simulation.space 113 | for shape in space.shapes: 114 | if(isinstance(shape, Car)): 115 | cars.append(shape) 116 | 117 | simulation.canvasWindow.canvas.remove(shape.ky) 118 | shape.ky = None 119 | 120 | for car in cars: 121 | simulation.space.remove(car.body, car) 122 | 123 | simulation.removeCallbacks() 124 | space_copy = simulation.space.copy() 125 | simulation.addCallbacks() 126 | 127 | for car in cars: 128 | simulation.space.add(car.body, car) 129 | 130 | # Try exporting level 131 | try: 132 | with open(file_path, "wb") as f: 133 | pickle.dump(space_copy, f, pickle.HIGHEST_PROTOCOL) 134 | # If exception occured print something 135 | except: 136 | InfoPopup("Something went wrong!", "Level export", PN.DANGER_ICON) 137 | # If everything went ok 138 | else: 139 | for shape in space.shapes: 140 | paintObject(shape, simulation.canvasWindow) 141 | InfoPopup("Level successfully exported!", "Level export", PN.INFO_ICON) 142 | 143 | simulation.startPhysicsThread() 144 | simulation.canvasWindow.startDrawing() 145 | 146 | @classmethod 147 | def importLevel(self, simulation): 148 | pathname = os.path.abspath(os.path.dirname(sys.argv[0]))+"/levels" 149 | if not os.path.exists(pathname): 150 | os.makedirs(pathname) 151 | 152 | # Create filedialog 153 | root = tk.Tk() 154 | root.withdraw() 155 | file_path = filedialog.askopenfilename(initialdir = pathname,title = "Load level", defaultextension=".lvl", filetypes=self.LEVEL_TYPES) 156 | 157 | if(file_path != ""): 158 | # Try importing level 159 | try: 160 | with open(file_path, "rb") as f: 161 | loaded_space = pickle.load(f) 162 | # print("DEBUG: External space loaded") 163 | # If exception occured print something 164 | except: 165 | InfoPopup("Something went wrong!", "Level import", PN.DANGER_ICON) 166 | # If everything went ok 167 | else: 168 | simulation.deleteSpace() 169 | simulation.space.add_post_step_callback(simulation.loadSpace, loaded_space) 170 | 171 | InfoPopup("Level successfully imported!", "Level import", PN.INFO_ICON) 172 | 173 | @classmethod 174 | def exportLevelSilent(self, simulation, path): 175 | if(path != ""): 176 | simulation.canvasWindow.stopDrawing() 177 | simulation.endPhysicsThread() 178 | 179 | cars = [] 180 | space = simulation.space 181 | for shape in space.shapes: 182 | if(isinstance(shape, Car)): 183 | cars.append(shape) 184 | 185 | simulation.canvasWindow.canvas.remove(shape.ky) 186 | shape.ky = None 187 | 188 | simulation.removeCallbacks() 189 | for car in cars: 190 | simulation.space.remove(car.body, car) 191 | if(isinstance(car, CarAI)): 192 | car.deleteRaycasts(simulation) 193 | 194 | space_copy = simulation.space.copy() 195 | 196 | simulation.addCallbacks() 197 | for car in cars: 198 | simulation.space.add(car.body, car) 199 | if(isinstance(car, CarAI)): 200 | car.loadRaycasts(simulation) 201 | 202 | # Try exporting level 203 | try: 204 | with open(path, "wb") as f: 205 | pickle.dump(space_copy, f, pickle.HIGHEST_PROTOCOL) 206 | 207 | # If exception occured print something 208 | except: 209 | InfoPopup("Something went wrong!", "Level export", PN.DANGER_ICON) 210 | simulation.startPhysicsThread() 211 | simulation.canvasWindow.startDrawing() 212 | return False 213 | 214 | # If everything went ok 215 | else: 216 | for shape in space.shapes: 217 | paintObject(shape, simulation.canvasWindow) 218 | InfoPopup("Level successfully exported!", "Level export", PN.INFO_ICON) 219 | simulation.startPhysicsThread() 220 | simulation.canvasWindow.startDrawing() 221 | return True 222 | else: 223 | return False 224 | 225 | @classmethod 226 | def importLevelSilent(self, simulation, path): 227 | if(path != ""): 228 | loaded_space = None 229 | 230 | try: # Try importing level 231 | with open(path, "rb") as f: 232 | loaded_space = pickle.load(f) 233 | except: # If exception occured print something 234 | simulation.startPhysicsThread() 235 | simulation.canvasWindow.startDrawing() 236 | return False 237 | else: # If everything went ok 238 | simulation.deleteSpace() 239 | simulation.space.add_post_step_callback(simulation.loadSpace, loaded_space) 240 | 241 | return True 242 | else: 243 | return False 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /ai/GameController.py: -------------------------------------------------------------------------------- 1 | from objs.kivyObjs import distXY 2 | from ai.DQN import DQN 3 | from ai.SGA import SGA 4 | from objs.GameObjects import StaticGameObject 5 | 6 | from kivy.app import App 7 | 8 | import numpy as np 9 | import math 10 | 11 | class GameController(): 12 | IDLE_STATE = 0 13 | LEARNING_STATE = 1 14 | TESTING_STATE = 2 15 | PLAYING_STATE = 3 16 | 17 | REINFORCEMENT_LEARN = "reinforcement" 18 | 19 | def __init__(self, simulation): 20 | self.simulation = simulation 21 | self.bestPercentage = 0.2 22 | self.game = 0 23 | 24 | # Skip step 25 | self.memoryStep = 0; 26 | self.memoryAction = None; 27 | 28 | # Training vars 29 | self.learningType = self.REINFORCEMENT_LEARN 30 | self.state = self.IDLE_STATE 31 | self.stepLimit = 5000 32 | self.startSteps = 0 33 | 34 | # Learning objects 35 | self.DQN = DQN() 36 | 37 | # Cars 38 | self.testCar = None 39 | 40 | # Network to Export 41 | self.exportModel = None 42 | 43 | # Training speed / Show speed 44 | self.trainingSpeed = 8 45 | self.showSpeed = 2 46 | 47 | # Get neural model 48 | def getNetwork(self): 49 | model = self.getNetworkFromCar() 50 | if(model != None): 51 | return model 52 | else: 53 | return self.exportModel 54 | 55 | # Set neural network 56 | def setNetwork(self, model): 57 | if(model == None): 58 | return 59 | 60 | self.resetNetwork() 61 | self.exportModel = model 62 | self.simulation.gameController.updateGUI() 63 | 64 | # New network 65 | def resetNetwork(self): 66 | config = App.get_running_app().config 67 | 68 | # Training vars 69 | learn_type = config.get('AI', 'learn_type') 70 | if(learn_type == "DQN"): 71 | self.learningType = self.REINFORCEMENT_LEARN 72 | 73 | # Reset all learning object and values 74 | self.DQN = DQN(float(config.get('DQN','dqn_discount_factor')), float(config.get('DQN','dqn_exploration_min')), 75 | float(config.get('DQN','dqn_exploration_max')), float(config.get('DQN','dqn_exploration_decay')), 76 | int(config.get('DQN','dqn_batch_size'))) 77 | 78 | self.exportModel = None 79 | self.game = 0 80 | 81 | # Reset graphics 82 | self.updateGUI() 83 | 84 | # Start idling 85 | self.simulation.canvasWindow.changeGameState("exit-idle") 86 | 87 | # Get neural model from states 88 | def getNetworkFromCar(self): 89 | # Return car if learning 90 | if(self.state == self.LEARNING_STATE): 91 | # via Reinforcement learn 92 | if(self.learningType == self.REINFORCEMENT_LEARN): 93 | if(self.DQN.dqnCar != None): 94 | return self.DQN.dqnCar.model 95 | 96 | # Return testCar if testing 97 | elif(self.state == self.TESTING_STATE): 98 | if(self.testCar != None): 99 | return self.testCar.model 100 | 101 | return None 102 | 103 | 104 | # Changes state to train 105 | def startTrain(self, *args): 106 | # Pause physics thread 107 | self.simulation.endPhysicsThread() 108 | 109 | # Change speed of physics simulation 110 | self.simulation.update_frequency = self.simulation.fast_update_frequency 111 | 112 | # Prepare game vars 113 | self.simulation.simulationSpeed = self.trainingSpeed 114 | self.game = 0 115 | 116 | # Prepare DQN 117 | if(self.learningType == self.REINFORCEMENT_LEARN): 118 | self.DQN.respawnCar(self.simulation) 119 | 120 | # Change state last --> Threaded 121 | self.state = self.LEARNING_STATE 122 | 123 | # Reset start steps 124 | self.startSteps = self.simulation.space.steps 125 | 126 | # Resume physics thread 127 | self.simulation.startPhysicsThread() 128 | 129 | # Changes state to testing 130 | def startTest(self): 131 | # Pause physics thread 132 | self.simulation.endPhysicsThread() 133 | 134 | # Load model first 135 | model = self.exportModel 136 | 137 | # Prepare Controller 138 | self.testCar = None 139 | 140 | # Prepare enviroment simulation 141 | self.simulation.simulationSpeed = self.showSpeed 142 | self.simulation.removeCars() 143 | 144 | # Copy model or create new 145 | if(model != None): 146 | # Copy brain 147 | car = self.simulation.addCarAI(model) 148 | else: 149 | # Generate random 150 | car = self.simulation.addCarAI() 151 | 152 | self.testCar = car 153 | 154 | # Set camera 155 | self.simulation.canvasWindow.selectedCar = car 156 | 157 | # Change state last --> Threaded 158 | self.state = self.TESTING_STATE 159 | 160 | # Resume physics thread 161 | self.simulation.startPhysicsThread() 162 | 163 | # Changes state to free play 164 | def startFreePlay(self): 165 | # Pause physics thread 166 | self.simulation.endPhysicsThread() 167 | 168 | car = self.simulation.addPlayer() 169 | 170 | # Set camera 171 | self.simulation.canvasWindow.selectedCar = car 172 | 173 | # Change state last --> Threaded 174 | self.state = self.PLAYING_STATE 175 | 176 | # Resume physics thread 177 | self.simulation.startPhysicsThread() 178 | 179 | # Changes state to IDLE 180 | def startIdle(self): 181 | # Pause physics thread 182 | self.simulation.endPhysicsThread() 183 | 184 | # Change speed of physics simulation 185 | self.simulation.update_frequency = self.simulation.normal_update_frequency 186 | 187 | # Save model first, but do not save model when playing --> nothing to save 188 | if(self.state != self.PLAYING_STATE): 189 | self.exportModel = self.getNetworkFromCar() 190 | 191 | self.state = self.IDLE_STATE 192 | self.simulation.simulationSpeed = self.showSpeed 193 | self.simulation.removeCars() 194 | 195 | # Resume physics thread 196 | self.simulation.startPhysicsThread() 197 | 198 | # Handle car collisions 199 | def handleCollision(self, car, otherObject): 200 | # If collide object is sensor, dont call for collision 201 | if(otherObject.sensor): 202 | return 203 | 204 | # Learning 205 | if(self.state == self.LEARNING_STATE): 206 | car.kill(self.simulation.canvasWindow) 207 | 208 | # Testing 209 | elif(self.state == self.TESTING_STATE): 210 | car.respawn(self.simulation) 211 | 212 | # Playing 213 | elif(self.state == self.PLAYING_STATE and otherObject.objectType == StaticGameObject.FINISH): 214 | car.respawn(self.simulation) 215 | 216 | # GUI Update --> call at the end of the Run 217 | def updateGUI(self): 218 | guiObject = self.simulation.canvasWindow.window.stateInfoBar 219 | 220 | # Update all GUI 221 | if(self.learningType == self.REINFORCEMENT_LEARN): 222 | # Change graph names 223 | guiObject.changeGraphLabel(guiObject.graph1, "Deaths", "Reward") 224 | guiObject.changeGraphLabel(guiObject.graph2, "Deaths", "Exploration rate") 225 | 226 | # Add point to graphs 227 | if(self.DQN.dqnCar != None): 228 | guiObject.addPlotPointRight(self.DQN.deathCount, self.DQN.dqnCar.reward) 229 | guiObject.addPlotPointLeft(self.DQN.deathCount, (self.DQN.exploration_rate*100)) 230 | 231 | # Change values overall 232 | guiObject.setValue1("Learning type", "DQN") 233 | guiObject.setValue2("Memories", len(self.DQN.memory)) 234 | 235 | # Current run 236 | guiObject.setValue3("Exploration rate", round((self.DQN.exploration_rate*100),2)) 237 | guiObject.setValue4("Max reward", round(self.DQN.highestReward,2)) 238 | 239 | else: # Testing stage 240 | pass 241 | 242 | 243 | # End of the Run (Car died or timer is up) 244 | def endOfRun(self): 245 | self.game += 1 246 | 247 | # Update GUI 248 | self.updateGUI() 249 | 250 | if(self.learningType == self.REINFORCEMENT_LEARN): 251 | # Prepare for next game 252 | self.DQN.respawnCar(self.simulation) 253 | 254 | # Reset timer 255 | self.startSteps = self.simulation.space.steps 256 | 257 | # Training loop --> run every game 258 | def loop(self): 259 | # Training model 260 | if(self.state == self.LEARNING_STATE): 261 | if(self.DQN.dqnCar == None): return 262 | 263 | # If time ran out end the round without punish 264 | if((self.simulation.space.steps-self.startSteps) > self.stepLimit): 265 | self.endOfRun() 266 | 267 | # DQN Learning 268 | elif(self.learningType == self.REINFORCEMENT_LEARN): 269 | # If car died, punish it and then end the round 270 | if(self.DQN.dqnCar.isDead): 271 | self.DQN.step(self.simulation) 272 | self.endOfRun() 273 | 274 | # Current test continues --> Did NOT died 275 | else: 276 | if(self.memoryStep == 0): 277 | self.DQN.step(self.simulation) 278 | else: 279 | self.DQN.dqnCar.takeLastAction() 280 | 281 | # Testing model 282 | elif(self.state == self.TESTING_STATE): 283 | if(self.testCar != None): 284 | car = self.testCar 285 | 286 | if(self.memoryStep == 0): 287 | observation = np.array(car.calculateRaycasts(self.simulation.space)) 288 | car.takeAction(dist=observation, graph=self.simulation.graph) 289 | else: 290 | car.takeLastAction() 291 | 292 | # Reset memory step 293 | if(self.state != self.PLAYING_STATE): 294 | self.memoryStep += 1 295 | if(self.memoryStep >= 3): 296 | self.memoryStep = 0; -------------------------------------------------------------------------------- /windows/Simulation.py: -------------------------------------------------------------------------------- 1 | # Kivy 2 | from kivy.graphics import Color 3 | 4 | # Tensorflow 5 | import tensorflow as tf 6 | 7 | # Pymunk 8 | import cffi 9 | import pymunk 10 | import pymunk.autogeometry 11 | from pymunk.vec2d import Vec2d 12 | 13 | import threading as th 14 | from time import sleep, time 15 | import os 16 | import sys 17 | import math 18 | import random 19 | 20 | from objs.GameObjects import StaticGameObject 21 | from objs.CarAI import CarAI 22 | from objs.Car import Car 23 | from objs.kivyObjs import distXY, centerPoint, paintObject 24 | from windows.ImportExport import IELevel 25 | from ai.GameController import GameController 26 | 27 | random.seed(5) 28 | 29 | class Simulation(): 30 | 31 | def __init__(self, canvasWindow): 32 | # Important values 33 | self.step = 1/60. 34 | self.canvasWindow = canvasWindow 35 | 36 | # Learning vars 37 | self.gameController = GameController(self) 38 | 39 | # Create thread for physics 40 | self.stopThread = True 41 | 42 | # Tensorflow computational graph 43 | self.graph = tf.get_default_graph() 44 | 45 | # Default level 46 | self.defaultLevel = os.path.abspath(os.path.dirname(sys.argv[0]))+"/levels/conti.lvl" 47 | 48 | #Simulation speed cap 49 | self.fast_update_frequency = 1000 50 | self.normal_update_frequency = 150 51 | self.update_frequency = self.normal_update_frequency 52 | 53 | # Create new space 54 | def setupSpace(self): 55 | # print("DEBUG: Setting up space") 56 | 57 | self.endPhysicsThread() 58 | 59 | self.space = space = pymunk.Space() 60 | 61 | self.addCallbacks() 62 | 63 | space.gravity = 0, 0 64 | space.sleep_time_threshold = 0.3 65 | space.steps = 0 66 | 67 | self.startPhysicsThread() 68 | 69 | # print("DEBUG: Space set up") 70 | return space 71 | 72 | # Prepare collisions callbacks 73 | def addCallbacks(self): 74 | self.handler = self.space.add_collision_handler(0,0) 75 | self.handler.begin = self.coll_begin 76 | self.handler.pre_solve = self.coll_pre 77 | self.handler.post_solve = self.coll_post 78 | self.handler.separate = self.coll_separate 79 | 80 | # Remove collisions callbacks 81 | def removeCallbacks(self): 82 | self.handler.begin = None 83 | self.handler.pre_solve = None 84 | self.handler.post_solve = None 85 | self.handler.separate = None 86 | self.handler = None 87 | 88 | # Delete everything from space & canvas 89 | def deleteSpace(self): 90 | for shape in self.space.shapes: 91 | self.canvasWindow.canvas.remove(shape.ky) 92 | 93 | self.setupSpace() 94 | 95 | # Load shapes from space to current space 96 | def loadSpace(self, space, loadedSpace): 97 | for shape in loadedSpace.shapes: 98 | self.space.add(shape.copy().body, shape.copy()) 99 | 100 | # print("DEBUG: Space loaded") 101 | self.addCallbacks() 102 | # print("DEBUG: Space callbacks added") 103 | 104 | for shape in self.space.shapes: 105 | paintObject(shape, self.canvasWindow) 106 | # print("DEBUG: Shapes drawn") 107 | 108 | # First start, setup space and import level 109 | def start(self): 110 | self.setupSpace() 111 | 112 | # Load default level 113 | def loadDefaultLevel(self): 114 | # Check if default level exists 115 | if(not IELevel.importLevelSilent(self, self.defaultLevel)): 116 | # Spawning objects 117 | StaticGameObject(StaticGameObject.BARRIER).createSegment((0,0), (2000,0), 20, self.canvasWindow) 118 | StaticGameObject(StaticGameObject.BARRIER).createSegment((2000,0), (2000,1000), 20, self.canvasWindow) 119 | StaticGameObject(StaticGameObject.BARRIER).createSegment((2000,1000), (0,1000), 20, self.canvasWindow) 120 | StaticGameObject(StaticGameObject.BARRIER).createSegment((0,0), (0,1000), 20, self.canvasWindow) 121 | 122 | start = StaticGameObject(StaticGameObject.START, rgba=(0,.8,0,1)) 123 | start.createSegment((100,400), (100,600), 20, self.canvasWindow) 124 | 125 | finish = StaticGameObject(StaticGameObject.FINISH, rgba=(.8,0,0,1)) 126 | finish.createSegment((1800,400), (1800,600), 20, self.canvasWindow) 127 | 128 | """ 129 | Threaded loop functions 130 | """ 131 | # Start thread --> MUST BE CALLED FROM MAIN THREAD 132 | def startPhysicsThread(self): 133 | if(hasattr(self, "thread")): 134 | if(self.thread != None): 135 | self.endPhysicsThread() 136 | print("Tried to start a new thread when there is still a running thread!") # DEBUG 137 | 138 | self.stopThread = False 139 | self.thread = th.Thread(target=self.physicsThread, name="PhysicsThread") 140 | self.thread.daemon = True 141 | self.thread.start() 142 | 143 | # End thread --> MUST BE CALLED FROM MAIN THREAD 144 | def endPhysicsThread(self): 145 | if(hasattr(self, "thread")): 146 | if(self.thread != None): 147 | self.stopThread = True 148 | self.thread.join() 149 | self.thread = None 150 | else: 151 | print("Tried to end non-existing thread! NONE") # DEBUG 152 | else: 153 | print("Tried to end non-existing thread! ATTR") # DEBUG 154 | 155 | # Thread function for physics 156 | def physicsThread(self): 157 | _last_time = 0 158 | while True: 159 | _now = time() 160 | if(_now - _last_time > (1/self.update_frequency)): 161 | self.update() 162 | _last_time = _now 163 | else: 164 | sleep(_now - _last_time) 165 | 166 | if(self.stopThread): 167 | break 168 | 169 | return True 170 | 171 | # Main looping function - Run in thread 172 | def update(self): 173 | # Game logic loop 174 | if(self.gameController != None): 175 | self.gameController.loop() 176 | 177 | # Physics loop 178 | self.stepSpace() 179 | 180 | # Step simulation space 181 | def stepSpace(self): 182 | for shape in self.space.shapes: 183 | if(not shape.body.is_sleeping): 184 | if(hasattr(shape, "raycast")): continue 185 | 186 | # If there is friction set by class use it 187 | if(isinstance(shape, Car)): 188 | friction = shape.ground_friction 189 | angular_friction = shape.angular_friction 190 | else: 191 | friction = 0.9 192 | angular_friction = 0.9 193 | 194 | # Zero-out velocity vector if it is approaching 0 195 | if(shape.body.velocity.length < 0.001): 196 | shape.body.velocity = Vec2d(0,0) 197 | 198 | # Apply friction every step 199 | shape.body.velocity *= 1 - (self.step*friction) 200 | shape.body.angular_velocity *= 1 - (self.step*angular_friction) 201 | 202 | # Stepping space simul 203 | self.space.step(self.step) 204 | self.space.steps += 1 205 | 206 | 207 | """ 208 | Enviroment functions 209 | """ 210 | # Reset level 211 | def resetLevel(self): 212 | # Delete all Cars from level and canvas 213 | for shape in self.space.shapes: 214 | if(isinstance(shape, Car)): 215 | # Delete any raycast if CarAI 216 | if(isinstance(shape, CarAI)): 217 | shape.deleteRaycasts(self) 218 | self.space.remove(shape.body, shape) 219 | self.canvasWindow.canvas.remove(shape.ky) 220 | 221 | # Adding 222 | def addSegment(self, a, b, radius, typeVal, collisions, rgba, change="change"): 223 | if(typeVal == "Finish"): 224 | segment = StaticGameObject(StaticGameObject.FINISH, rgba=rgba) 225 | elif(typeVal == "Start"): 226 | segment = StaticGameObject(StaticGameObject.START, rgba=rgba) 227 | elif(collisions == False): 228 | segment = StaticGameObject(StaticGameObject.NOBARRIER, rgba=rgba) 229 | else: 230 | segment = StaticGameObject(StaticGameObject.BARRIER, rgba=rgba) 231 | 232 | segment.createSegment(a, b, radius, self.canvasWindow) 233 | self.repaintObjects() 234 | 235 | # Undo system 236 | if(change == "change"): 237 | self.canvasWindow.changes.append(segment.shape) 238 | 239 | def addCircle(self, a, radius, typeVal, collisions, rgba, change="change"): 240 | if(typeVal == "Finish"): 241 | circle = StaticGameObject(StaticGameObject.FINISH, rgba=rgba) 242 | elif(typeVal == "Start"): 243 | circle = StaticGameObject(StaticGameObject.START, rgba=rgba) 244 | elif(collisions == False): 245 | circle = StaticGameObject(StaticGameObject.NOBARRIER, rgba=rgba) 246 | else: 247 | circle = StaticGameObject(StaticGameObject.BARRIER, rgba=rgba) 248 | 249 | circle.createCircle(a, radius, self.canvasWindow) 250 | self.repaintObjects() 251 | 252 | # Undo system 253 | if(change == "change"): 254 | self.canvasWindow.changes.append(circle.shape) 255 | 256 | def addBox(self, points, typeVal, collisions, rgba, change="change"): 257 | if(typeVal == "Finish"): 258 | box = StaticGameObject(StaticGameObject.FINISH, rgba=rgba) 259 | elif(typeVal == "Start"): 260 | box = StaticGameObject(StaticGameObject.START, rgba=rgba) 261 | elif(collisions == False): 262 | box = StaticGameObject(StaticGameObject.NOBARRIER, rgba=rgba) 263 | else: 264 | box = StaticGameObject(StaticGameObject.BARRIER, rgba=rgba) 265 | 266 | box.createBoxPoints(points, self.canvasWindow) 267 | self.repaintObjects() 268 | 269 | # Undo system 270 | if(change == "change"): 271 | self.canvasWindow.changes.append(box.shape) 272 | 273 | # Delete object from space 274 | def deleteObject(self, obj, change="change"): 275 | self.space.remove(obj) 276 | self.canvasWindow.canvas.remove(obj.ky) 277 | 278 | # Undo system 279 | if(change == "change"): 280 | self.canvasWindow.changes.append(obj) 281 | 282 | # Repaint all object and keep layering in mind 283 | def repaintObjects(self): 284 | cars = [] 285 | 286 | for shape in self.space.shapes: 287 | if(hasattr(shape, "ky")): 288 | self.canvasWindow.canvas.remove(shape.ky) 289 | 290 | for shape in self.space.shapes: 291 | self.canvasWindow.canvas.add(Color(rgba=shape.rgba)) 292 | if(isinstance(shape, Car)): 293 | cars.append(shape) 294 | else: 295 | self.canvasWindow.canvas.add(shape.ky) 296 | 297 | # Paint cars on top of everything 298 | for car in cars: 299 | car.paint(self.canvasWindow) 300 | 301 | # Add one car as an AI model 302 | def addCarAI(self, model=None): 303 | point = self.findSpawnpoint() 304 | if(point != None): 305 | car = CarAI(10, (100,50), self.findSpawnpoint(), ground_friction=1, angular_friction=3, model=model) 306 | car.createRaycasts(self) 307 | self.space.add(car.body, car) 308 | self.repaintObjects() 309 | return car 310 | else: 311 | self.canvasWindow.t_signal = self.canvasWindow.TS_SPAWN_ERROR 312 | return None 313 | 314 | # Add one car as a player 315 | def addPlayer(self): 316 | point = self.findSpawnpoint() 317 | if(point != None): 318 | car = Car(10, (100,50), self.findSpawnpoint(), ground_friction=1, angular_friction=3) 319 | self.space.add(car.body, car) 320 | self.repaintObjects() 321 | return car 322 | else: 323 | self.canvasWindow.t_signal = self.canvasWindow.TS_SPAWN_ERROR 324 | return None 325 | 326 | # Returns all car instances from space.shapes 327 | def getCars(self): 328 | cars = [] 329 | for shape in self.space.shapes: 330 | if(isinstance(shape, Car)): 331 | cars.append(shape) 332 | 333 | return cars 334 | 335 | # Load cars from an array 336 | def loadCars(self, space, *_): 337 | cars = self.canvasWindow.savedCars 338 | 339 | # If empty array return 340 | if(cars == []): 341 | return 342 | 343 | for car in cars: 344 | self.space.add(car.body, car) 345 | if(isinstance(car, CarAI)): 346 | car.loadRaycasts(self) 347 | car.paint(self.canvasWindow) 348 | 349 | self.canvasWindow.selectedCar = cars[0] 350 | self.canvasWindow.savedCars = [] 351 | 352 | 353 | # Remove player from space (all car class instances) 354 | def removeCars(self): 355 | for shape in self.space.shapes: 356 | if(isinstance(shape, Car)): 357 | # Delete any raycast if CarAI 358 | if(isinstance(shape, CarAI)): 359 | shape.deleteRaycasts(self) 360 | self.space.remove(shape.body, shape) 361 | self.canvasWindow.canvas.remove(shape.ky) 362 | 363 | self.canvasWindow.selectedCar = None 364 | 365 | # Find spawnpoint for car 366 | def findSpawnpoint(self): 367 | spawnPoint = None 368 | 369 | for shape in self.space.shapes: 370 | if(hasattr(shape, "objectType")): 371 | if(shape.objectType == StaticGameObject.START): 372 | spawnPoint = self.getCenterPos(shape) 373 | 374 | return spawnPoint 375 | 376 | # Find nearest finish 377 | def findNearestFinish(self, point): 378 | finishPoint = None 379 | 380 | for shape in self.space.shapes: 381 | if(hasattr(shape, "objectType")): 382 | if(shape.objectType == StaticGameObject.FINISH): 383 | finishPoint = self.getCenterPos(shape) 384 | 385 | return finishPoint 386 | 387 | # Get center position of any shape 388 | def getCenterPos(self, shape): 389 | point = None 390 | if(isinstance(shape, pymunk.Segment)): 391 | point = centerPoint(shape.a, shape.b) 392 | elif(isinstance(shape, pymunk.Circle)): 393 | point = shape.body.position 394 | elif(isinstance(shape, pymunk.Poly)): 395 | vertices = shape.get_vertices() 396 | point = centerPoint(centerPoint(vertices[0], vertices[1]), centerPoint(vertices[2], vertices[3])) 397 | 398 | return point 399 | 400 | # Shift layer shif --> direction, spec --> special TOP, BOTTOM --> ALL THE WAY 401 | def shiftLayer(self, obj, shift, spec): 402 | temp = None 403 | tempShapes = [] 404 | splitIndex = None 405 | 406 | if(spec): 407 | for shape in self.space.shapes: 408 | if(shape == obj): 409 | temp = obj 410 | else: 411 | tempShapes.append(shape) 412 | 413 | self.space.remove(shape) 414 | 415 | if(shift > 0): 416 | for tempShape in tempShapes: 417 | self.space.add(tempShape) 418 | self.space.add(temp) 419 | else: 420 | self.space.add(temp) 421 | for tempShape in tempShapes: 422 | self.space.add(tempShape) 423 | 424 | 425 | self.repaintObjects() 426 | else: 427 | # Save moving object, Save all object from moving one --> tempShapes 428 | for index, shape in enumerate(self.space.shapes): 429 | if(shape == obj): 430 | splitIndex = index 431 | temp = shape 432 | 433 | if(shift < 0 and splitIndex != 0): 434 | tempShapes.append(self.space.shapes[splitIndex-1]) 435 | 436 | elif(splitIndex != None): 437 | tempShapes.append(shape) 438 | 439 | if(len(tempShapes) != 0): 440 | # Delete all tempShapes from space 441 | for tempShape in tempShapes: 442 | self.space.remove(tempShape) 443 | 444 | # Delete moving object from space 445 | self.space.remove(temp) 446 | 447 | # Add first from tempShapes and then moving object itself 448 | if(shift > 0): 449 | self.space.add(tempShapes[0]) 450 | 451 | self.space.add(temp) 452 | 453 | # Add rest of tempShapes except of the first one 454 | for index, tempShape in enumerate(tempShapes): 455 | if(index == 0 and shift > 0): 456 | continue 457 | 458 | self.space.add(tempShape) 459 | 460 | # Repaint everything 461 | self.repaintObjects() 462 | 463 | # Get layer of object 464 | def getLayer(self, obj): 465 | for index, shape in enumerate(self.space.shapes): 466 | if(shape == obj): 467 | return index 468 | 469 | # Collistions handlers 470 | def coll_begin(self, arbiter, space, data): 471 | if(isinstance(arbiter.shapes[0], Car) or isinstance(arbiter.shapes[1], Car)): 472 | car = None 473 | otherObject = None 474 | if(isinstance(arbiter.shapes[0], Car)): 475 | car = arbiter.shapes[0] 476 | otherObject = arbiter.shapes[1] 477 | else: 478 | car = arbiter.shapes[1] 479 | otherObject = arbiter.shapes[0] 480 | 481 | if(otherObject.objectType != StaticGameObject.START): 482 | self.gameController.handleCollision(car, otherObject) 483 | return True 484 | return True 485 | 486 | def coll_pre(self, arbiter, space, data): 487 | return True 488 | 489 | def coll_post(self, arbiter, space, data): 490 | pass 491 | 492 | def coll_separate(self, arbiter, space, date): 493 | pass 494 | -------------------------------------------------------------------------------- /templates/canvas.kv: -------------------------------------------------------------------------------- 1 | # :kivy 1.0 2 | 3 | 4 | 5 | : 6 | title: 'Pick a Color' 7 | size_hint: 1.0, 0.6 8 | id: popupcolor 9 | 10 | BoxLayout: 11 | orientation: 'vertical' 12 | 13 | ColorPicker: 14 | id: colorpicker 15 | size_hint: 1.0, 1.0 16 | 17 | Button: 18 | text: 'PICK AND CLOSE' 19 | color: 0.435, 0.725, 0.56, 1 20 | background_color: 0, 0.26, 0.27, 1 21 | size_hint: 1.0, 0.2 22 | on_press: popupcolor.on_press_dismiss(colorpicker) 23 | 24 | : 25 | Label: 26 | text: "Level exported!" 27 | Button: 28 | text: 'Close' 29 | on_release: root.dismiss() 30 | 31 | : 32 | size_hint_y: None 33 | thickness: 4 34 | margin: 4 35 | height: self.thickness + 2 * self.margin 36 | color: .8, .8, .8, 1 37 | canvas: 38 | Color: 39 | rgba: self.color 40 | Rectangle: 41 | pos: self.x + self.margin, self.y + self.margin + 1 42 | size: self.width - 2 * self.margin , self.thickness 43 | 44 | 45 | : 46 | id: objectMenu 47 | BoxLayout: 48 | id: mainLayout 49 | orientation: "vertical" 50 | size: root.scaleFactor,root.scaleFactor*2 51 | pos: (root.width-self.width,(root.height-self.height)-48) 52 | canvas: 53 | Color: 54 | rgba: .1, .1, .1, 1 55 | Rectangle: 56 | size: self.size[0]+4,self.size[1]+4 57 | pos: self.pos[0]-4,self.pos[1]-4 58 | 59 | Color: 60 | rgba: .142, .142, .142, 1 61 | Rectangle: 62 | size: self.size 63 | pos: self.pos 64 | # radius: [(root.width+root.height)/ root.scaleFactor,] 65 | 66 | ToggleButton: 67 | id: deleteBtn 68 | text: "Delete" 69 | size_hint: 1,0.2 70 | on_state: 71 | root.changeTool(self.state, "delete") 72 | 73 | ToggleButton: 74 | id: addBtn 75 | text: "Add" 76 | size_hint: 1,0.2 77 | on_state: 78 | root.changeTool(self.state, "add") 79 | 80 | 81 | Separator: 82 | color: 0,0,0,0 83 | 84 | GridLayout: 85 | id: inObjectMenu 86 | rows: 4 87 | spacing: 2 88 | # padding: 0,50,0,50 89 | size: self.parent.size 90 | pos: self.parent.pos 91 | 92 | : 93 | id: editMenu 94 | BoxLayout: 95 | id: mainLayout 96 | orientation: "vertical" 97 | size: root.scaleFactor,root.scaleFactor*2 98 | pos: (0,(root.height-self.height)-48) 99 | canvas: 100 | Color: 101 | rgba: .1, .1, .1, 1 102 | Rectangle: 103 | size: self.size[0]+4,self.size[1]+4 104 | pos: self.pos[0],self.pos[1]-4 105 | 106 | Color: 107 | rgba: .142, .142, .142, 1 108 | Rectangle: 109 | size: self.size 110 | pos: self.pos 111 | # radius: [(root.width+root.height)/ root.scaleFactor,] 112 | 113 | # Deleting 114 | Label: 115 | text: "Tools" 116 | size_hint: 1,0.15 117 | Button: 118 | id: deleteBtn 119 | text: "Delete object" 120 | size_hint: 1,0.2 121 | on_press: 122 | root.deleteObject() 123 | 124 | Separator: 125 | color: .2, .2, .2, 1 126 | 127 | # Change layer position 128 | Label: 129 | text: "Layer shift" 130 | size_hint: 1,0.15 131 | BoxLayout: 132 | orientation: "horizontal" 133 | size_hint: 1,0.3 134 | 135 | BoxLayout: 136 | orientation: "vertical" 137 | Button: 138 | text: "Up" 139 | on_press: 140 | root.shiftLayer(1) 141 | Button: 142 | text: "Top" 143 | on_press: 144 | root.shiftLayer(1, True) 145 | 146 | Label: 147 | id: layerLabel 148 | text: "?" 149 | 150 | BoxLayout: 151 | orientation: "vertical" 152 | Button: 153 | text: "Down" 154 | on_press: 155 | root.shiftLayer(-1) 156 | Button: 157 | text: "Bottom" 158 | on_press: 159 | root.shiftLayer(-1, True) 160 | 161 | Separator: 162 | color: .2, .2, .2, 1 163 | 164 | # Properties editing 165 | Label: 166 | text: "Properties" 167 | size_hint: 1,0.15 168 | GridLayout: 169 | id: inEditMenu 170 | rows: 4 171 | spacing: 2 172 | # padding: 0,50,0,50 173 | size: self.parent.size 174 | pos: self.parent.pos 175 | 176 | : 177 | id: startMenu 178 | BoxLayout: 179 | id: mainLayout 180 | orientation: "vertical" 181 | size: root.scaleFactor,root.scaleFactor*2 182 | pos: (0,(root.height-self.height)-48) 183 | canvas: 184 | Color: 185 | rgba: .1, .1, .1, 1 186 | Rectangle: 187 | size: self.size[0]+4,self.size[1]+4 188 | pos: self.pos[0],self.pos[1]-4 189 | 190 | Color: 191 | rgba: .142, .142, .142, 1 192 | Rectangle: 193 | size: self.size 194 | pos: self.pos 195 | # radius: [(root.width+root.height)/ root.scaleFactor,] 196 | 197 | BoxLayout: 198 | orientation: "vertical" 199 | spacing: 5 200 | padding: 10,0,10,0 201 | Button: 202 | text: "Learn" 203 | font_size: root.scaleFactor/12.5 204 | on_press: 205 | root.game.changeGameState(1) 206 | 207 | Button: 208 | text: "Test" 209 | font_size: root.scaleFactor/12.5 210 | on_press: 211 | root.game.changeGameState(2) 212 | 213 | Button: 214 | text: "Play" 215 | font_size: root.scaleFactor/12.5 216 | on_press: 217 | root.game.changeGameState(3) 218 | 219 | Separator: 220 | color: .2, .2, .2, 1 221 | 222 | BoxLayout: 223 | orientation: "vertical" 224 | spacing: 5 225 | padding: 10,0,10,0 226 | 227 | Button: 228 | text: "Neural Model" 229 | on_press: 230 | app.start_layers() 231 | 232 | Button: 233 | text: "AI Settings" 234 | on_press: 235 | app.open_settings() 236 | 237 | Button: 238 | text: "Reset settings" 239 | on_press: 240 | app.config_reset() 241 | 242 | 243 | : 244 | id: statebar 245 | 246 | canvas: 247 | Color: 248 | rgba: .1, .1, .1, 1 249 | Rectangle: 250 | pos: 0,0 251 | size: root.width, root.scaleFactor/5 252 | 253 | GridLayout: 254 | pos: 0,0 255 | size: root.width, root.scaleFactor/5 256 | cols: 3 257 | 258 | Label: 259 | id: steps 260 | text: "Steps: 0" 261 | font_size: root.scaleFactor/12.5 262 | 263 | Label: 264 | id: tool 265 | text: "Idle" 266 | font_size: root.scaleFactor/12.5 267 | 268 | ToggleButton: 269 | id: info 270 | text: "Model info" 271 | font_size: root.scaleFactor/12.5 272 | 273 | on_state: 274 | root.screen.toggleStateInfoBar(self.state) 275 | 276 | 277 | : 278 | id: stateInfoBar 279 | 280 | BoxLayout: 281 | orientation: "vertical" 282 | pos: 0,root.scaleFactor/5 283 | size: root.width, root.scaleFactor 284 | 285 | canvas: 286 | Color: 287 | rgba: .1, .1, .1, 1 288 | Rectangle: 289 | pos: self.pos[0], self.pos[1]+5 290 | size: self.size 291 | 292 | Color: 293 | rgba: .08, .08, .08, 1 294 | Rectangle: 295 | pos: self.pos 296 | size: self.size 297 | 298 | # Base layout 299 | BoxLayout: 300 | orientation: 'horizontal' 301 | pos: self.pos 302 | size: self.size 303 | 304 | # Graph2 305 | BoxLayout: 306 | id: graph2 307 | orientation: "vertical" 308 | 309 | # Graph2 310 | 311 | # Graph1 312 | BoxLayout: 313 | id: graph1 314 | orientation: "vertical" 315 | 316 | # Graph1 317 | 318 | # Other info 319 | BoxLayout: 320 | id: info 321 | orientation: "vertical" 322 | 323 | canvas: 324 | Color: 325 | rgba: .1, .1, .1, 1 326 | Rectangle: 327 | pos: self.pos[0], self.pos[1] 328 | size: self.size[0], self.size[1] 329 | 330 | # Info about current session 331 | BoxLayout: 332 | orientation: "vertical" 333 | 334 | canvas: 335 | Color: 336 | rgba: .08, .08, .08, 1 337 | RoundedRectangle: 338 | pos: self.pos[0]+self.pos[0]*(1/16), self.pos[1]+10 339 | size: self.size[0]*(3/4), self.size[1]-10 340 | radius: (10,10,10,10) 341 | 342 | Label: 343 | id: val1 344 | text: "Learning type: " 345 | Label: 346 | id: val2 347 | text: "" 348 | Separator: 349 | color: 1,1,1,0 350 | 351 | Separator: 352 | color: 1,1,1,0 353 | 354 | # Overall info 355 | BoxLayout: 356 | orientation: "vertical" 357 | 358 | canvas: 359 | Color: 360 | rgba: .08, .08, .08, 1 361 | RoundedRectangle: 362 | pos: self.pos[0]+self.pos[0]*(1/16), self.pos[1]+10 363 | size: self.size[0]*(3/4), self.size[1]-10 364 | radius: (10,10,10,10) 365 | 366 | Label: 367 | id: val3 368 | text: "" 369 | Label: 370 | id: val4 371 | text: "" 372 | Separator: 373 | color: 1,1,1,0 374 | 375 | 376 | 377 | : 378 | title: 'Level editor' 379 | with_previous: False 380 | app_icon: 'icons/levelicon.png' 381 | app_icon_width: 40 382 | app_icon_height: 40 383 | 384 | # width: len(self.title) * 10 385 | # size_hint_x: None 386 | 387 | : 388 | # Editor action bar 389 | BoxLayout: 390 | orientation: "horizontal" 391 | size: root.width, root.height 392 | 393 | # Special bar 394 | ActionBar: 395 | id: mainActionBarGame 396 | pos_hint: {"top": 1} 397 | 398 | ActionView: 399 | use_separator: True 400 | HiddenIcon_ActionPrevious: 401 | title: '' 402 | width: 60 403 | size_hint_x: None 404 | app_icon: 'icons/levelicon.png' 405 | font_size: root.scaleFactor/12.5 406 | 407 | ActionButton: 408 | text: 'Exit editor' 409 | font_size: root.scaleFactor/12.5 410 | on_press: 411 | root.screen.endLevelEditor() 412 | 413 | # Tools 414 | ActionGroup: 415 | mode: 'spinner' 416 | text: 'Tools' 417 | font_size: root.scaleFactor/12.5 418 | 419 | # Obect menu 420 | ActionButton: 421 | text: 'Tool window' 422 | font_size: root.scaleFactor/12.5 423 | on_press: 424 | root.screen.toggleObjectMenu() 425 | 426 | # Common bar 427 | ActionView: 428 | HiddenIcon_ActionPrevious: 429 | title: '' 430 | width: 0 431 | app_icon: '' 432 | font_size: root.scaleFactor/12.5 433 | 434 | # Level interaction buttons 435 | ActionGroup: 436 | mode: 'spinner' 437 | text: 'Level' 438 | font_size: root.scaleFactor/12.5 439 | ActionButton: 440 | text: 'New Level' 441 | font_size: root.scaleFactor/12.5 442 | on_press: 443 | root.game.simulation.deleteSpace() 444 | ActionButton: 445 | text: 'Save Level' 446 | font_size: root.scaleFactor/12.5 447 | on_press: 448 | root.game.exportFile() 449 | ActionButton: 450 | text: 'Load Level' 451 | font_size: root.scaleFactor/12.5 452 | on_press: 453 | root.game.importFile() 454 | 455 | 456 | # Model interaction buttons 457 | ActionGroup: 458 | mode: 'spinner' 459 | text: 'Model' 460 | font_size: root.scaleFactor/12.5 461 | ActionButton: 462 | text: 'New Model' 463 | font_size: root.scaleFactor/12.5 464 | on_press: 465 | root.game.simulation.gameController.resetNetwork() 466 | ActionButton: 467 | text: 'Save Model' 468 | font_size: root.scaleFactor/12.5 469 | on_press: 470 | root.game.exportNetwork() 471 | ActionButton: 472 | text: 'Load Model' 473 | font_size: root.scaleFactor/12.5 474 | on_press: 475 | root.game.importNetwork() 476 | 477 | # View 478 | ActionGroup: 479 | mode: 'spinner' 480 | text: 'View' 481 | font_size: root.scaleFactor/12.5 482 | ActionButton: 483 | text: 'Center Camera' 484 | minimum_width: root.scaleFactor*0.8 485 | font_size: root.scaleFactor/12.5 486 | on_press: 487 | root.game.changeCamera("center") 488 | 489 | ActionSeparator: 490 | 491 | ActionButton: 492 | text: 'Follow mode' 493 | minimum_width: root.scaleFactor*0.8 494 | font_size: root.scaleFactor/12.5 495 | on_press: 496 | root.game.changeCamera("follow") 497 | ActionButton: 498 | text: 'Free mode' 499 | minimum_width: root.scaleFactor*0.8 500 | font_size: root.scaleFactor/12.5 501 | on_press: 502 | root.game.changeCamera("free") 503 | 504 | 505 | : 506 | # Game action bar 507 | BoxLayout: 508 | orientation: "horizontal" 509 | size: root.width, root.height 510 | 511 | # Special Bar 512 | ActionBar: 513 | id: mainActionBarGame 514 | pos_hint: {"top": 1} 515 | 516 | ActionView: 517 | use_separator: True 518 | HiddenIcon_ActionPrevious: 519 | title: '' 520 | width: 60 521 | size_hint_x: None 522 | app_icon: 'icons/gameicon.png' 523 | font_size: root.scaleFactor/12.5 524 | 525 | ActionButton: 526 | text: 'Level Editor' 527 | font_size: root.scaleFactor/12.5 528 | on_press: 529 | root.screen.startLevelEditor() 530 | 531 | ActionButton: 532 | text: 'Back' 533 | font_size: root.scaleFactor/12.5 534 | on_press: 535 | root.game.changeGameState("exit") 536 | 537 | # Common bar 538 | ActionView: 539 | HiddenIcon_ActionPrevious: 540 | title: '' 541 | width: 0 542 | app_icon: '' 543 | font_size: root.scaleFactor/12.5 544 | 545 | # Level interaction buttons 546 | ActionGroup: 547 | mode: 'spinner' 548 | text: 'Level' 549 | font_size: root.scaleFactor/12.5 550 | ActionButton: 551 | text: 'New Level' 552 | font_size: root.scaleFactor/12.5 553 | on_press: 554 | root.game.simulation.deleteSpace() 555 | ActionButton: 556 | text: 'Save Level' 557 | font_size: root.scaleFactor/12.5 558 | on_press: 559 | root.game.exportFile() 560 | ActionButton: 561 | text: 'Load Level' 562 | font_size: root.scaleFactor/12.5 563 | on_press: 564 | root.game.importFile() 565 | 566 | 567 | # Model interaction buttons 568 | ActionGroup: 569 | mode: 'spinner' 570 | text: 'Model' 571 | font_size: root.scaleFactor/12.5 572 | ActionButton: 573 | text: 'New Model' 574 | font_size: root.scaleFactor/12.5 575 | on_press: 576 | root.game.simulation.gameController.resetNetwork() 577 | ActionButton: 578 | text: 'Save Model' 579 | font_size: root.scaleFactor/12.5 580 | on_press: 581 | root.game.exportNetwork() 582 | ActionButton: 583 | text: 'Load Model' 584 | font_size: root.scaleFactor/12.5 585 | on_press: 586 | root.game.importNetwork() 587 | 588 | # View 589 | ActionGroup: 590 | mode: 'spinner' 591 | text: 'View' 592 | font_size: root.scaleFactor/12.5 593 | ActionButton: 594 | text: 'Center Camera' 595 | minimum_width: root.scaleFactor*0.8 596 | font_size: root.scaleFactor/12.5 597 | on_press: 598 | root.game.changeCamera("center") 599 | 600 | ActionSeparator: 601 | 602 | ActionButton: 603 | text: 'Follow mode' 604 | minimum_width: root.scaleFactor*0.8 605 | font_size: root.scaleFactor/12.5 606 | on_press: 607 | root.game.changeCamera("follow") 608 | ActionButton: 609 | text: 'Free mode' 610 | minimum_width: root.scaleFactor*0.8 611 | font_size: root.scaleFactor/12.5 612 | on_press: 613 | root.game.changeCamera("free") -------------------------------------------------------------------------------- /windows/GameWidgets.py: -------------------------------------------------------------------------------- 1 | import random 2 | random.seed(5) 3 | 4 | import kivy 5 | from kivy.app import App 6 | from kivy.uix.widget import Widget 7 | from kivy.properties import ObjectProperty, NumericProperty 8 | from kivy.uix.screenmanager import ScreenManager, Screen 9 | from kivy.core.window import Window 10 | from kivy.uix.gridlayout import GridLayout 11 | from kivy.uix.popup import Popup 12 | from kivy.uix.colorpicker import ColorPicker 13 | from kivy.uix.image import Image 14 | from kivy.uix.behaviors import ToggleButtonBehavior 15 | from kivy.graphics import Color, Rectangle 16 | from kivy_garden.graph import Graph, MeshLinePlot 17 | 18 | # Animation 19 | from kivy.animation import Animation 20 | from kivy.uix.button import Button 21 | from kivy.uix.dropdown import DropDown 22 | from kivy.uix.label import Label 23 | 24 | # Custom classes 25 | from windows.Simulation import Simulation 26 | from windows.CanvasHandler import CanvasHandler 27 | from objs.GameObjects import StaticGameObject 28 | 29 | import math 30 | 31 | # Special widget class with screen and scaling 32 | class GameWidget(Widget): 33 | # Factor of scaling (Fonts, UI) !!! DOES NOT AFFECT RESOLUTION SCALER ONLY MULTIPLIES THE EFFECT !!! 34 | scaleFactor = NumericProperty(200) 35 | 36 | def __init__(self, game, screen, **kwargs): 37 | super(GameWidget, self).__init__(**kwargs) 38 | self.game = game 39 | self.screen = screen 40 | 41 | class BGLabel(Label): 42 | def __init__(self, **kwargs): 43 | super().__init__(**kwargs) 44 | self.bind(pos=self.draw) 45 | self.bind(size=self.draw) 46 | self.draw() 47 | 48 | def draw(self, *args): 49 | if(self.canvas == None): return 50 | self.canvas.before.clear() 51 | with self.canvas.before: 52 | Color(rgba=(.1,.1,.1,1)) 53 | Rectangle(pos=self.pos, size=self.size) 54 | 55 | # Action bars (Game, Level editor) 56 | class ToolBar(GameWidget): 57 | def __init__(self, manager, game, screen, **kwargs): 58 | super(ToolBar, self).__init__(game, screen, **kwargs) 59 | self.manager = manager 60 | class ToolBarGame(ToolBar): 61 | pass 62 | class ToolBarEditor(ToolBar): 63 | pass 64 | 65 | # Stator, shows ingame vars on the bottom of the UI 66 | class StateBar(GameWidget): 67 | pass 68 | 69 | # Extends stator if needed 70 | class StateInfoBar(GameWidget): 71 | DEFAULT_YMAX = 0.00001 72 | DEFAULT_YMIN = 0 73 | DEFAULT_XMAX = 1 74 | DEFAULT_XMIN = -0 75 | DEFAULT_X_TICKS_MAJOR = 1 76 | DEFAULT_Y_TICKS_MAJOR = 1 77 | 78 | def __init__(self, game, screen, **kwargs): 79 | super(StateInfoBar, self).__init__(game, screen, **kwargs) 80 | 81 | # Right graph 82 | self.graph1 = graph1 = Graph(xlabel='Game', ylabel='Reward', 83 | x_ticks_major=self.DEFAULT_X_TICKS_MAJOR, y_ticks_major=self.DEFAULT_Y_TICKS_MAJOR, 84 | y_grid_label=True, x_grid_label=True, padding=5, 85 | x_grid=True, y_grid=True, xmin=self.DEFAULT_XMIN, xmax=self.DEFAULT_XMAX, ymin=self.DEFAULT_YMIN, ymax=self.DEFAULT_YMAX) 86 | 87 | self.plot1 = plot1 = MeshLinePlot(color=[1, 0, 0, 1]) 88 | plot1.points = [] 89 | graph1.add_plot(plot1) 90 | 91 | self.ids["graph1"].add_widget(graph1) 92 | 93 | # Left graph 94 | self.graph2 = graph2 = Graph(xlabel='Generation', ylabel='MaxFitness', 95 | x_ticks_major=self.DEFAULT_X_TICKS_MAJOR, y_ticks_major=self.DEFAULT_Y_TICKS_MAJOR, 96 | y_grid_label=True, x_grid_label=True, padding=5, 97 | x_grid=True, y_grid=True, xmin=self.DEFAULT_XMIN, xmax=self.DEFAULT_XMAX, ymin=self.DEFAULT_YMIN, ymax=self.DEFAULT_YMAX) 98 | 99 | self.plot2 = plot2 = MeshLinePlot(color=[0, 1, 0, 1]) 100 | plot2.points = [] 101 | graph2.add_plot(plot2) 102 | 103 | self.ids["graph2"].add_widget(graph2) 104 | 105 | # Reset plot on graph: 106 | def resetPlot(self, graph, plot): 107 | graph.xmax = self.DEFAULT_XMAX 108 | graph.xmin = self.DEFAULT_XMIN 109 | graph.ymax = self.DEFAULT_YMAX 110 | graph.ymin = self.DEFAULT_YMIN 111 | graph.x_ticks_major = self.DEFAULT_X_TICKS_MAJOR 112 | graph.y_ticks_major = self.DEFAULT_Y_TICKS_MAJOR 113 | plot.points = [] 114 | 115 | # Change graph labels 116 | def changeGraphLabel(self, graph, xlabel=None, ylabel=None): 117 | if(xlabel != None): 118 | graph.xlabel = xlabel 119 | 120 | if(ylabel != None): 121 | graph.ylabel = ylabel 122 | 123 | # Left graph 124 | def addPlotPointLeft(self, x, y, plot=None): 125 | if(plot == None): 126 | plot = self.plot2 127 | 128 | self.addPlotPoint(self.graph2, plot, x, y) 129 | 130 | # Right graph 131 | def addPlotPointRight(self, x, y, plot=None): 132 | if(plot == None): 133 | plot = self.plot1 134 | 135 | self.addPlotPoint(self.graph1, plot, x, y) 136 | 137 | # Rescale y axis on graph 138 | def rescaleY(self, graph, y): 139 | # Rescaling of y axis 140 | if(y > (graph.ymax - (graph.ymax/10))): 141 | graph.ymax = int(math.ceil(y + (y/10))) # Move max more up by fraction of y --> better look 142 | 143 | if(y > abs(graph.ymin)): 144 | graph.y_ticks_major = math.floor(y/2) 145 | 146 | if(y < (graph.ymin - (graph.ymin/10))): 147 | graph.ymin = int(math.ceil(y + (y/10))) # Move max more up by fraction of y --> better look 148 | 149 | if(abs(y) > graph.ymax): 150 | graph.y_ticks_major = abs(math.floor(y/2)) 151 | 152 | # Add point to graph 153 | def addPlotPoint(self, graph, plot, x, y): 154 | # Add data to graph 155 | plot.points.append((x, y)) 156 | 157 | self.rescaleY(graph, y) 158 | 159 | # Rescaling of x axis 160 | if((x+1) > graph.xmax): 161 | graph.xmax = x+1 162 | 163 | # Change major points only dividable by 5 164 | if(x % 5 == 0): 165 | graph.x_ticks_major = math.floor(x/5) if((x/5) > 1) else 1 166 | 167 | # Reset graph if x < xmax 168 | else: 169 | self.resetPlot(graph, plot) 170 | plot.points.append((x, y)) 171 | self.rescaleY(graph, y) 172 | 173 | # Changes top value 174 | def setValue1(self, name, value): 175 | self.ids["val1"].text = str(name)+": "+str(value) 176 | 177 | # Changes second value 178 | def setValue2(self, name, value): 179 | self.ids["val2"].text = str(name)+": "+str(value) 180 | 181 | # Changes third value 182 | def setValue3(self, name, value): 183 | self.ids["val3"].text = str(name)+": "+str(value) 184 | 185 | # Changes bottom value 186 | def setValue4(self, name, value): 187 | self.ids["val4"].text = str(name)+": "+str(value) 188 | 189 | # Menu for choosing mode --> Play, Train, Test 190 | class StartMenu(GameWidget): 191 | pass 192 | 193 | # Object adding 194 | class ObjectMenu(GameWidget): 195 | visible = False 196 | 197 | def __init__(self, game, screen, **kwargs): 198 | super(ObjectMenu, self).__init__(game, screen, **kwargs) 199 | # Defaults 200 | self.colorVal = (0.2,0.2,0.2, 1) 201 | self.collisionsVal = True 202 | self.shapeVal = "Segment" 203 | self.typeVal = "Barrier" 204 | 205 | # Grids 206 | self.shapeGrid = GridLayout(cols=2) 207 | self.typeGrid = GridLayout(cols=2) 208 | self.colorGrid = GridLayout(cols=2) 209 | self.collisionGrid = GridLayout(cols=2) 210 | 211 | # Shape Label + Dropdown 212 | self.shapeDropDown = ShapeDropDown(self) 213 | self.shapeButton = Button(text="Segment") 214 | self.shapeButton.bind(on_release=self.shapeDropDown.open) 215 | self.shapeDropDown.bind(on_select=lambda instance, x: setattr(self.shapeButton, 'text', x)) 216 | self.shapeGrid.add_widget(Label(text="Shape: ")) 217 | self.shapeGrid.add_widget(self.shapeButton) 218 | 219 | # Type Label + Dropdown 220 | self.typeDropDown = TypeDropDown(self) 221 | self.typeButton = Button(text="Barrier") 222 | self.typeButton.bind(on_release=self.typeDropDown.open) 223 | self.typeDropDown.bind(on_select=lambda instance, x: setattr(self.typeButton, 'text', x)) 224 | self.typeGrid.add_widget(Label(text="Type: ")) 225 | self.typeGrid.add_widget(self.typeButton) 226 | 227 | # COLOR 228 | self.colorGrid.add_widget(Label(text="Color: ")) 229 | self.colorButton = Button(background_normal=('Image.extension'), background_color=self.colorVal, text="", on_release=lambda instance: self.showColorpicker()) 230 | self.colorGrid.add_widget(self.colorButton) 231 | 232 | # Collisions 233 | self.collisionGrid.add_widget(Label(text="Collisions: ")) 234 | self.collisionGrid.add_widget(ToggleButton(self)) 235 | 236 | # Add all layouts 237 | self.ids["inObjectMenu"].add_widget(self.shapeGrid) 238 | self.ids["inObjectMenu"].add_widget(self.typeGrid) 239 | self.ids["inObjectMenu"].add_widget(self.colorGrid) 240 | self.ids["inObjectMenu"].add_widget(self.collisionGrid) 241 | 242 | # Change game tools base on buttons 243 | def changeTool(self, state, btn): 244 | if(state == "down"): 245 | if(btn == "add"): 246 | self.game.changeTool("add") 247 | elif(btn == "delete"): 248 | self.game.changeTool("delete") 249 | else: 250 | self.game.changeTool("move") 251 | 252 | # Change buttons state 253 | def changeButtonState(self, state, btn): 254 | if(state == "down"): 255 | if(btn == "delete"): 256 | self.ids["deleteBtn"].state = "down" 257 | elif(btn == "add"): 258 | self.ids["addBtn"].state = "down" 259 | elif(state == "normal"): 260 | if(btn == "delete"): 261 | self.ids["deleteBtn"].state = "normal" 262 | elif(btn == "add"): 263 | self.ids["addBtn"].state = "normal" 264 | 265 | 266 | def showColorpicker(self): 267 | popup = PopupColor(self) 268 | popup_color = ColorPicker() 269 | popup.color = self.colorVal 270 | popup.open() 271 | 272 | # Resulting callbacks 273 | def resultShape(self, shape): 274 | self.shapeVal = shape 275 | 276 | def resultType(self, typeVal): 277 | self.typeVal = typeVal 278 | if(self.typeVal == "Finish"): 279 | self.colorVal = (.8,0,0,1) 280 | elif(self.typeVal == "Start"): 281 | self.colorVal = (0,.8,0,1) 282 | else: 283 | self.colorVal = (.2,.2,.2, 1) 284 | 285 | self.colorButton.background_color = self.colorVal 286 | 287 | def resultColor(self, color): 288 | if(color[3] != None and color[3] != 0): 289 | self.colorVal = (color[0],color[1],color[2],color[3]) 290 | else: 291 | self.colorVal = (color[0],color[1],color[2],1) 292 | self.colorButton.background_color = color 293 | 294 | def resultCollisions(self, val): 295 | self.collisionsVal = val 296 | 297 | def getData(self): 298 | data = {"shape": self.shapeVal, "type": self.typeVal, "color": self.colorVal, "collisions": self.collisionsVal} 299 | return data 300 | 301 | # Object editing 302 | class EditMenu(GameWidget): 303 | visible = False 304 | 305 | def __init__(self, game, screen, **kwargs): 306 | super(EditMenu, self).__init__(game, screen, **kwargs) 307 | # Defaults 308 | self.editObject = None 309 | self.highlight = None 310 | self.colorVal = (0.2,0.2,0.2, 1) 311 | self.collisionsVal = True 312 | self.shapeVal = "Segment" 313 | self.typeVal = "Barrier" 314 | 315 | # Grids 316 | self.typeGrid = GridLayout(cols=2) 317 | self.colorGrid = GridLayout(cols=2) 318 | self.collisionGrid = GridLayout(cols=2) 319 | 320 | # Type Label + Dropdown 321 | self.typeDropDown = TypeDropDown(self) 322 | self.typeButton = Button(text="Barrier") 323 | self.typeButton.bind(on_release=self.typeDropDown.open) 324 | self.typeDropDown.bind(on_select=lambda instance, x: setattr(self.typeButton, 'text', x)) 325 | self.typeGrid.add_widget(Label(text="Type: ")) 326 | self.typeGrid.add_widget(self.typeButton) 327 | 328 | # COLOR 329 | self.colorGrid.add_widget(Label(text="Color: ")) 330 | self.colorButton = Button(background_normal=('Image.extension'), text="", on_release=lambda instance: self.showColorpicker()) 331 | self.colorGrid.add_widget(self.colorButton) 332 | 333 | # Collisions 334 | self.collisionGrid.add_widget(Label(text="Collisions: ")) 335 | self.collisionToggleButton = ToggleButton(self) 336 | self.collisionGrid.add_widget(self.collisionToggleButton) 337 | 338 | # Add all layouts 339 | self.ids["inEditMenu"].add_widget(self.typeGrid) 340 | self.ids["inEditMenu"].add_widget(self.colorGrid) 341 | self.ids["inEditMenu"].add_widget(self.collisionGrid) 342 | 343 | # Get object 344 | def setEditObject(self, obj): 345 | if(obj == None): 346 | # Disable widget 347 | self.disableMenu() 348 | return 349 | 350 | elif(self.editObject == None): 351 | self.screen.add_widget(self) 352 | 353 | self.editObject = obj 354 | 355 | # Create highlight 356 | if(self.highlight != None): 357 | self.game.canvas.remove(self.highlight) 358 | self.highlight = self.game.highlightObject(self.editObject) 359 | 360 | if(hasattr(self.editObject, "objectType")): 361 | # Set layer height 362 | self.ids["layerLabel"].text = str(self.game.simulation.getLayer(self.editObject)) 363 | 364 | # Set type button 365 | if(self.editObject.objectType == StaticGameObject.BARRIER or self.editObject.objectType == StaticGameObject.NOBARRIER): 366 | self.typeButton.text = "Barrier" 367 | elif(self.editObject.objectType == StaticGameObject.START): 368 | self.typeButton.text = "Start" 369 | elif(self.editObject.objectType == StaticGameObject.FINISH): 370 | self.typeButton.text = "Finish" 371 | 372 | # Set color 373 | self.colorButton.background_color = self.editObject.rgba 374 | 375 | # Set collisions 376 | if(self.editObject.sensor): 377 | self.collisionToggleButton.forceState(False) 378 | else: 379 | self.collisionToggleButton.forceState(True) 380 | 381 | else: 382 | # Propably chosen a car --> Cant edit 383 | self.editObject = None 384 | self.screen.remove_widget(self) 385 | 386 | # Shift selected object 387 | def shiftLayer(self, shift, spec=False): 388 | if(self.editObject != None): 389 | self.game.simulation.shiftLayer(self.editObject, shift, spec) 390 | 391 | self.ids["layerLabel"].text = str(self.game.simulation.getLayer(self.editObject)) 392 | 393 | # Deletes selected object 394 | def deleteObject(self): 395 | self.game.simulation.deleteObject(self.editObject) 396 | self.disableMenu() 397 | 398 | # Disable widget 399 | def disableMenu(self): 400 | if(self.highlight != None): 401 | self.game.canvas.remove(self.highlight) 402 | self.screen.remove_widget(self) 403 | 404 | self.highlight = None 405 | self.editObject = None 406 | 407 | # Color picker 408 | def showColorpicker(self): 409 | popup = PopupColor(self) 410 | popup_color = ColorPicker() 411 | popup.color = self.colorVal 412 | popup.open() 413 | 414 | # Resulting callbacks 415 | def resultType(self, typeVal): 416 | self.typeVal = typeVal 417 | 418 | if(self.editObject != None): 419 | if(typeVal == "Finish"): 420 | # When changed to finish force collisions 421 | self.editObject.objectType = StaticGameObject.FINISH 422 | self.editObject.sensor = False 423 | self.collisionToggleButton.forceState(True) 424 | elif(typeVal == "Start"): 425 | # When changed to start force no collisions 426 | self.editObject.objectType = StaticGameObject.START 427 | self.editObject.sensor = True 428 | self.collisionToggleButton.forceState(False) 429 | elif(typeVal == "Barrier"): 430 | # When changed to barrier check sensor behav 431 | if(self.editObject.sensor): 432 | self.editObject.objectType = StaticGameObject.NOBARRIER 433 | else: 434 | self.editObject.objectType = StaticGameObject.BARRIER 435 | 436 | 437 | def resultColor(self, color): 438 | if(color[3] != None): 439 | self.colorVal = (color[0],color[1],color[2],color[3]) 440 | else: 441 | self.colorVal = (color[0],color[1],color[2],1) 442 | self.colorButton.background_color = color 443 | 444 | if(self.editObject != None): 445 | self.editObject.rgba = self.colorVal 446 | self.game.canvas.remove(self.editObject.ky) 447 | self.game.canvas.add(Color(rgba=self.editObject.rgba)) 448 | self.game.canvas.add(self.editObject.ky) 449 | 450 | self.game.simulation.repaintObjects() 451 | 452 | def resultCollisions(self, val): 453 | self.collisionsVal = val 454 | 455 | if(self.editObject != None): 456 | self.editObject.sensor = not val 457 | 458 | 459 | def getData(self): 460 | data = {"type": self.typeVal, "color": self.colorVal, "collisions": self.collisionsVal} 461 | return data 462 | 463 | # Custom GUI objects 464 | class ActivationDropDown(DropDown): 465 | def __init__(self, func, **kwargs): 466 | super().__init__(**kwargs) 467 | 468 | self.func = func 469 | self.add_widget(Button(text="relu", size_hint_y = None, height=44, on_release=lambda instance: self.select("relu"))) 470 | self.add_widget(Button(text="sigmoid", size_hint_y = None, height=44, on_release=lambda instance: self.select("sigmoid"))) 471 | self.add_widget(Button(text="linear", size_hint_y = None, height=44, on_release=lambda instance: self.select("linear"))) 472 | 473 | def on_select(self, data): 474 | self.func(data) 475 | 476 | # Custom GUI objects 477 | class ShapeDropDown(DropDown): 478 | def __init__(self, menu, **kwargs): 479 | super().__init__(**kwargs) 480 | self.menu = menu 481 | self.add_widget(Button(text="Segment", size_hint_y = None, height=44, on_release=lambda instance: self.select("Segment"))) 482 | self.add_widget(Button(text="Circle", size_hint_y = None, height=44, on_release=lambda instance: self.select("Circle"))) 483 | self.add_widget(Button(text="Box", size_hint_y = None, height=44, on_release=lambda instance: self.select("Box"))) 484 | 485 | def on_select(self, data): 486 | self.menu.resultShape(data) 487 | class TypeDropDown(DropDown): 488 | def __init__(self, menu, **kwargs): 489 | super().__init__(**kwargs) 490 | self.menu = menu 491 | self.add_widget(Button(text="Barrier", size_hint_y = None, height=44, on_release=lambda instance: self.select("Barrier"))) 492 | self.add_widget(Button(text="Finish", size_hint_y = None, height=44, on_release=lambda instance: self.select("Finish"))) 493 | self.add_widget(Button(text="Start", size_hint_y = None, height=44, on_release=lambda instance: self.select("Start"))) 494 | 495 | def on_select(self, data): 496 | self.menu.resultType(data) 497 | 498 | # Separator object 499 | class Separator(Widget): 500 | pass 501 | 502 | # Checkbox 503 | class ToggleButton(ToggleButtonBehavior, Image): 504 | def __init__(self, menu, **kwargs): 505 | super(ToggleButton, self).__init__(**kwargs) 506 | self.source = 'atlas://data/images/defaulttheme/checkbox_on' 507 | self.menu = menu 508 | 509 | def on_state(self, widget, value): 510 | if(value == 'down'): 511 | self.source = 'atlas://data/images/defaulttheme/checkbox_off' 512 | self.menu.resultCollisions(False) 513 | else: 514 | self.source = 'atlas://data/images/defaulttheme/checkbox_on' 515 | self.menu.resultCollisions(True) 516 | 517 | def forceState(self, state): 518 | if(state): 519 | self.source = 'atlas://data/images/defaulttheme/checkbox_on' 520 | else: 521 | self.source = 'atlas://data/images/defaulttheme/checkbox_off' 522 | 523 | # Popup 524 | class PopupColor(Popup): 525 | def __init__(self, menu, *args): 526 | super(PopupColor, self).__init__(*args) 527 | self.menu = menu 528 | 529 | def on_press_dismiss(self, colorpicker, *args): 530 | self.dismiss() 531 | color = colorpicker.color 532 | self.menu.resultColor(color) --------------------------------------------------------------------------------