jgmeyer / py-traffic-sim Goto Github PK
View Code? Open in Web Editor NEWA fun program to simulate vehicle traffic patterns in python
A fun program to simulate vehicle traffic patterns in python
For stress testing and debugging it would be helpful to display an FPS counter in the top right corner of the game screen.
Right now, there aren't examples of adding text in graphics.RoadScreen, but pygame should have documentation. We should not include this in graphics.RoadScreen, instead we should likely add this to a new module under src/ called ui/
where we'll eventually add more UI elements that will display above the screen. Still, code can be heavily borrowed from graphics.RoadScreen
as a reference.
We should gate this as well behind a new bool: settings.GameSettings.DISPLAY_FPS_COUNTER
https://stackoverflow.com/a/2530745/373196 has some tips on making this display more efficiently.
Right now I'm only linting with pylint. Let's be more forceful, so no one has to think twice about formatting their code.
Consider Python Black: https://github.com/psf/black
Relevant PyCon 2019 presentation: https://www.youtube.com/watch?v=esZLCuWs_2Y&feature=youtu.be
Check out: https://stackoverflow.com/questions/60345091/pyinstaller-with-pipenv
This ticket is to:
This ticket is not to:
Bug repros even as far back as 9861eb1.
Commonly happens between ENTER->ENTER and EXIT->EXIT nodes, however, one repro showed an ENTER->EXIT failure.
This is easiest to repro once completing a single loop, however I HAVE managed to cause it to fail prior to completing the loop. I have NOT managed to repro the issue once at least 1 intersection (tile w/ >= 3 segments) was present.
Sample error (from gif):
$ pipenv run python src/game.py
pygame 2.0.0.dev4 (SDL 2.0.10, python 3.7.4)
Hello from the pygame community. https://www.pygame.org/contribute.html
Traceback (most recent call last):
File "src/game.py", line 161, in <module>
game_loop(game_window, clock)
File "src/game.py", line 99, in game_loop
randomize_vehicle_paths(window, network)
File "src/game.py", line 116, in randomize_vehicle_paths
path = network.graph.shortest_path(v.last_node, random_node)
File "/Users/justian/scripts/py-traffic-sim/src/road/grid.py", line 371, in shortest_path
return nx.shortest_path(self.G, source=source_node, target=target_node)
File "/Users/justian/.local/share/virtualenvs/py-traffic-sim-0xsS5pXD/lib/python3.7/site-packages/networkx/algorithms/shortest_paths/generic.py", line 170, in shortest_path
paths = nx.bidirectional_shortest_path(G, source, target)
File "/Users/justian/.local/share/virtualenvs/py-traffic-sim-0xsS5pXD/lib/python3.7/site-packages/networkx/algorithms/shortest_paths/unweighted.py", line 226, in bidirectional_shortest_path
results = _bidirectional_pred_succ(G, source, target)
File "/Users/justian/.local/share/virtualenvs/py-traffic-sim-0xsS5pXD/lib/python3.7/site-packages/networkx/algorithms/shortest_paths/unweighted.py", line 294, in _bidirectional_pred_succ
raise nx.NetworkXNoPath("No path between %s and %s." % (source, target))
networkx.exception.NetworkXNoPath: No path between RoadSegmentNode(tile_index=(4, 13), dir=<Direction.LEFT: 3>, node_type=<RoadNodeType.EXIT: 1>, world_coords=(848, 281)) and RoadSegmentNode(tile_index=(7, 13), dir=<Direction.LEFT: 3>, node_type=<RoadNodeType.EXIT: 1>, world_coords=(848, 473)).
Requires:
Possible:
Checkpoint 1: Have all vehicles pause x seconds at intersection enter/exit node.
Checkpoint 2: Have intersection dictate which vehicle may pass through the intersection.
For cars to wait behind other cars when at an intersection.
Include:
Look for low-hanging fruit. This ticket isn't for doing a deep optimization, just to look for actions that could be obviously inefficient so far in the milestone.
It would be helpful to see which tile is being hovered over with the mouse cursor before placing a tile.
The actual design of the highlight is up to you. You can place it above or below the road, color in the entire box, etc. So long as the intention is clear.
Bonus points:
network.grid.get_neighbors(<params>) >= 1
Hints:
process_mouse_button_down()
in game.py for logic on processing mouse input.RoadScreen
in road/graphics.pygraphics.*Sprite
classes for examples on how to construct a sprite.graphics.RoadScreenLayers
for the different layers on the RoadScreen
.send_cursor()
where it may make sense in graphics.py which will take a cursor input and generate and place the sprite on the RoadScreen
.Our RoadNetwork (network.py) is composed of the following key components:
The TileGrid represents the roads (the black boxes) in the simulation.
The TravelGraph is the graph which outlines paths that Vehicles can take along the TileGrid
And Traffic handles the movement of Vehicles along the TravelGraph
Right now we can display travel edges when DISPLAY_TRAVEL_EDGES is set to True in src/settings.py. Let's add a similar flag called DISPLAY_ROAD_SEGMENT_NODES that will display the grid's RoadSegmentNodes for debugging purposes.
Color preference:
ROAD_SEGMENT_NODE_ENTER = (255, 0, 0) # red
ROAD_SEGMENT_NODE_EXIT = (0, 0, 255) # blue
RoadSegmentNodes already have world_coords, so it should be easy to draw them directly to road.graphics.RoadScreen.
We already have a layer for this: road.graphics.RoadScreenLayers.ROAD_SEGMENT_NODES, which is currently unused.
Check out the source in src/road/graphics.py for examples of how other Sprites have already been coded.
The most complex part of this project is likely figuring out how to send "updates" from the TravelGraph to the RoadScreen
See similar hints from #14 for where code may need to be added for this.
I am happy to answer any questions.
Clicking out of the game window and refocusing it resolves the issue. Until then, players cannot click-and-drag tile segments onto the grid.
Issue believed to be introduced in 27e478e.
Similar to #8, but should be simpler to implement.
Allow the user to scroll the playable grid. This does not yet include allowing the player to expand beyond the initial bounds set by game.GRID_WIDTH or game.GRID_HEIGHT.
CP1: Scroll with the arrow keys
CP2: Scroll using click+drag on the mouse
Right now, Vehicles move strictly linearly between EXIT and ENTER RoadSegmentNodes. While this works, it's not particularly realistic to actual traffic patterns.
Adding this likely will involve a lot of math. Essentially we want to have the vehicle follow an elliptical path from one EXIT node to another ENTER node when taking a right or left turn. Problem is, there isn't an easy way to calculate the arc length of an ellipse segment. It's all approximated using an integral.
I posted about this problem here: https://stackoverflow.com/questions/59783526/move-object-a-set-distance-along-an-elliptical-path?noredirect=1#comment105711071_59783526.
Possible solutions could include:
More research is needed to figure out which is the best solution.
I already isolated the Vehicle linear movement into physics.pathing.LinearTrajectory. Once we have a solution figured out, the logic can just go into a physics.pathing.EllipticalTrajectory and a Vehicle can swap between LinearTrajectory and EllipticalTrajectory as needed.
Simple change. Doesn't break anything currently, but good to consider for the future.
This requires that we already have working collision detection from #21.
Goals:
Hey, I am new to this. How do I add a Z-Wave adapter?
Dynaconf is extremely useful, but we have some values like TILE_WIDTH/HEIGHT and ROAD_WIDTH that we really don't want people modifying. Also, it seems like we're constantly passing conf
throughout the project just so most objects can get these values.
Is there a better way to clean this up? Maybe we should instead just import these into common.py
and reuse them that way? I'm not sure.
$ pipenv run pytest -vv
================================ test session starts ================================
platform darwin -- Python 3.7.6, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 -- /Users/justian/.local/share/virtualenvs/py-traffic-sim-0xsS5pXD/bin/python3.7
cachedir: .pytest_cache
rootdir: /Users/justian/scripts/py-traffic-sim
collected 0 items / 1 error
====================================== ERRORS =======================================
__________________ ERROR collecting src/road/tests/test_traffic.py __________________
src/road/tests/test_traffic.py:2: in <module>
from road.grid import RoadSegmentNode
src/road/grid.py:14: in <module>
from config import config
src/config.py:7: in <module>
Validator("TILE_HEIGHT", eq=settings.TILE_WIDTH),
../../.local/share/virtualenvs/py-traffic-sim-0xsS5pXD/lib/python3.7/site-packages/dynaconf/base.py:111: in __getattr__
return getattr(self._wrapped, name)
E AttributeError: 'Settings' object has no attribute 'TILE_WIDTH'
!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!
================================= 1 error in 1.03s ==================================
We should be passing a mock config in test_traffic.py. Regardless, we can't get this to run until grid.py no longer has a dependency on dynaconf.
We'll need to update RoadSegmentNode
to no longer require a module-level import of dynaconf.settings
.
I've started migrating constants into settings.GameSettings. Let's move these variables into GameSettings instead of defining them in game.py.
Goals:
Steps:
See https://gamedev.stackexchange.com/questions/39931/fast-accurate-2d-collision for a basic example.
Eventually, we'll want to make some of the settings configurable in-game. Let's set up settings.py as a singleton that can be referenced elsewhere in the code.
Let's add a way to prevent some constants from being modified once the game is run, as well, e.g TILE_WIDTH, TILE_HEIGHT, etc.
There could be some creative solutions to go about doing this elegantly.
settings
and replace with new config
moduleThings like:
Should prevent me or anyone else from accidentally modifying these values that we expect to be constant.
I have an Issue open with Dynaconf at: dynaconf/dynaconf#297
Let's move these out of road/common.py into settings.py. Several parts of the codebase will need to be refactored with this change, but it shouldn't be too difficult.
This should be tested by running the game and ensuring everything still functions correctly.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.