Compare commits
	
		
			3 Commits
		
	
	
		
			d46aa6af45
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9f86d060f0 | |||
| e2f4a0d2a5 | |||
| bc96cea2b9 | 
							
								
								
									
										59
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								README.md
									
									
									
									
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | <p align="center"> | ||||||
|  |     <img src="logo.png" width="300"> | ||||||
|  | </p> | ||||||
|  |  | ||||||
|  | # Rally Racer | ||||||
|  |  | ||||||
|  | This repository holds a sandbox driving simulation controllable via a network interface as a machine learning and data collection challenge. | ||||||
|  |  | ||||||
|  | # Installation | ||||||
|  | From the root of the repository, run | ||||||
|  | ```sh | ||||||
|  | uv sync | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | To run the game, you can use | ||||||
|  | ```sh | ||||||
|  | uv run main.py | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | # Generality | ||||||
|  | Launching [`main.py`](main.py) starts a race with a single car on the provided track.  | ||||||
|  | This track can be controlled either by keyboard (*WASD*) or by a socket interface.  | ||||||
|  | An example of such interface is included in the code in [*`scripts/recorder.py`*](scripts/recorder.py). To run it, simply use the following command: | ||||||
|  | ```sh | ||||||
|  | uv run -m scripts.recorder | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | # Sensing | ||||||
|  | The car sensing is available in two commodities: **raycasts** and **images**. These sensing snapshots are sent at 10 Hertz (i.e. 10 times a second). Due to this fact, correct reception of snapshot messages has to be done regularly. | ||||||
|  |  | ||||||
|  | # Communication protocol | ||||||
|  |  | ||||||
|  | A remote controller can be impemented using TCP socket connecting on localhost on port 5000. | ||||||
|  | Different commands can be issued to the race simulation to control the car. | ||||||
|  |  | ||||||
|  | These commands are declared in [`src/command.py`](src/command.py) | ||||||
|  |  | ||||||
|  | ##  Car controls | ||||||
|  | ```python | ||||||
|  | ControlCommand(control: CarControl, active: bool) | ||||||
|  | ``` | ||||||
|  | To simulate key press and control the car. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Controls | ||||||
|  |  | ||||||
|  | - <kbd>W</kbd> Move forward | ||||||
|  | - <kbd>S</kbd> Brake / move backward | ||||||
|  | - <kbd>A</kbd> Turn left | ||||||
|  | - <kbd>D</kbd> Turn right | ||||||
|  | - <kbd>F</kbd> Toggle FPS indicator | ||||||
|  | - <kbd>V</kbd> Toggle speedometer | ||||||
|  | - <kbd>R</kbd> Reset car | ||||||
|  | - <kbd>C</kbd> Toggle raycasts visibility | ||||||
|  | - <kbd>Esc</kbd> Quit | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Credits | ||||||
|  | This project is based on the repository [https://github.com/ISC-HEI/RallyRobotPilot_2025](https://github.com/ISC-HEI/RallyRobotPilot_2025), which is in turn based on [https://github.com/mandaw2014/Rally](https://github.com/mandaw2014/Rally) | ||||||
							
								
								
									
										143
									
								
								car.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								car.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||||
|  | <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||||
|  |  | ||||||
|  | <svg | ||||||
|  |    width="64" | ||||||
|  |    height="64" | ||||||
|  |    viewBox="0 0 64 64.000003" | ||||||
|  |    version="1.1" | ||||||
|  |    id="svg1" | ||||||
|  |    inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)" | ||||||
|  |    sodipodi:docname="car.svg" | ||||||
|  |    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||||
|  |    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||||
|  |    xmlns="http://www.w3.org/2000/svg" | ||||||
|  |    xmlns:svg="http://www.w3.org/2000/svg"> | ||||||
|  |   <sodipodi:namedview | ||||||
|  |      id="namedview1" | ||||||
|  |      pagecolor="#ffffff" | ||||||
|  |      bordercolor="#000000" | ||||||
|  |      borderopacity="0.25" | ||||||
|  |      inkscape:showpageshadow="2" | ||||||
|  |      inkscape:pageopacity="0.0" | ||||||
|  |      inkscape:pagecheckerboard="0" | ||||||
|  |      inkscape:deskcolor="#d1d1d1" | ||||||
|  |      inkscape:document-units="mm" | ||||||
|  |      showgrid="true" | ||||||
|  |      inkscape:zoom="11.313709" | ||||||
|  |      inkscape:cx="42.470601" | ||||||
|  |      inkscape:cy="34.957591" | ||||||
|  |      inkscape:window-width="1920" | ||||||
|  |      inkscape:window-height="1016" | ||||||
|  |      inkscape:window-x="0" | ||||||
|  |      inkscape:window-y="27" | ||||||
|  |      inkscape:window-maximized="1" | ||||||
|  |      inkscape:current-layer="layer1"> | ||||||
|  |     <inkscape:grid | ||||||
|  |        id="grid1" | ||||||
|  |        units="px" | ||||||
|  |        originx="0" | ||||||
|  |        originy="0" | ||||||
|  |        spacingx="1" | ||||||
|  |        spacingy="1" | ||||||
|  |        empcolor="#0099e5" | ||||||
|  |        empopacity="0.30196078" | ||||||
|  |        color="#0099e5" | ||||||
|  |        opacity="0.14901961" | ||||||
|  |        empspacing="8" | ||||||
|  |        enabled="true" | ||||||
|  |        visible="true" /> | ||||||
|  |   </sodipodi:namedview> | ||||||
|  |   <defs | ||||||
|  |      id="defs1"> | ||||||
|  |     <inkscape:path-effect | ||||||
|  |        effect="fillet_chamfer" | ||||||
|  |        id="path-effect3" | ||||||
|  |        is_visible="true" | ||||||
|  |        lpeversion="1" | ||||||
|  |        nodesatellites_param="F,0,0,1,0,1.0000002,0,1 @ F,0,1,1,0,1.0000002,0,1 @ F,0,1,1,0,1.0000002,0,1 @ F,0,1,1,0,1.0000002,0,1" | ||||||
|  |        radius="0" | ||||||
|  |        unit="px" | ||||||
|  |        method="auto" | ||||||
|  |        mode="F" | ||||||
|  |        chamfer_steps="1" | ||||||
|  |        flexible="false" | ||||||
|  |        use_knot_distance="true" | ||||||
|  |        apply_no_radius="true" | ||||||
|  |        apply_with_radius="true" | ||||||
|  |        only_selected="false" | ||||||
|  |        hide_knots="false" /> | ||||||
|  |     <inkscape:path-effect | ||||||
|  |        effect="fillet_chamfer" | ||||||
|  |        id="path-effect2" | ||||||
|  |        is_visible="true" | ||||||
|  |        lpeversion="1" | ||||||
|  |        nodesatellites_param="F,0,1,1,0,1.0000001,0,1 @ F,0,1,1,0,1.0000001,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,1,1,0,1.0000001,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1" | ||||||
|  |        radius="0" | ||||||
|  |        unit="px" | ||||||
|  |        method="auto" | ||||||
|  |        mode="F" | ||||||
|  |        chamfer_steps="1" | ||||||
|  |        flexible="false" | ||||||
|  |        use_knot_distance="true" | ||||||
|  |        apply_no_radius="true" | ||||||
|  |        apply_with_radius="true" | ||||||
|  |        only_selected="false" | ||||||
|  |        hide_knots="false" /> | ||||||
|  |   </defs> | ||||||
|  |   <g | ||||||
|  |      inkscape:label="Calque 1" | ||||||
|  |      inkscape:groupmode="layer" | ||||||
|  |      id="layer1"> | ||||||
|  |     <g | ||||||
|  |        id="g5" | ||||||
|  |        inkscape:label="car" | ||||||
|  |        transform="translate(-5.9999998,-7.9999997)"> | ||||||
|  |       <g | ||||||
|  |          id="g4" | ||||||
|  |          inkscape:label="wheels"> | ||||||
|  |         <path | ||||||
|  |            style="fill:#000000;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |            d="m 25.000001,31.999999 v -2 h 4 v 2 z" | ||||||
|  |            id="path2" | ||||||
|  |            sodipodi:nodetypes="ccccc" /> | ||||||
|  |         <path | ||||||
|  |            style="fill:#000000;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |            d="m 43.000001,31.999999 v -2 h 4 v 2 z" | ||||||
|  |            id="path2-3" | ||||||
|  |            sodipodi:nodetypes="ccccc" /> | ||||||
|  |         <path | ||||||
|  |            style="fill:#000000;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |            d="m 25.000001,49.999999 v -2 h 4 v 2 z" | ||||||
|  |            id="path2-1" | ||||||
|  |            sodipodi:nodetypes="ccccc" /> | ||||||
|  |         <path | ||||||
|  |            style="fill:#000000;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |            d="m 43.000001,49.999999 v -2 h 4 v 2 z" | ||||||
|  |            id="path2-3-2" | ||||||
|  |            sodipodi:nodetypes="ccccc" /> | ||||||
|  |       </g> | ||||||
|  |       <path | ||||||
|  |          style="fill:#e14324;fill-opacity:1;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |          d="m 16,33 v 13.999999 a 1.1327823,1.1327823 48.562508 0 0 0.992278,1.124035 l 7.007723,0.875965 h 24 l 11.003454,-0.916955 a 1.0867997,1.0867997 132.61818 0 0 0.996546,-1.083045 v -14 A 1.0867996,1.0867996 47.381818 0 0 59.003455,31.916954 L 48.000001,31 h -24 l -7.007723,0.875965 A 1.1327823,1.1327823 131.43749 0 0 16,33 Z" | ||||||
|  |          id="path1" | ||||||
|  |          inkscape:path-effect="#path-effect2" | ||||||
|  |          inkscape:original-d="m 16,32 v 15.999999 l 8.000001,1 h 24 l 12,-1 v -16 L 48.000001,31 h -24 z" | ||||||
|  |          inkscape:label="body" /> | ||||||
|  |       <path | ||||||
|  |          style="fill:#53170b;fill-opacity:1;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |          d="m 50.000001,33.500001 v 13 a 1.0867994,1.0867994 132.61819 0 1 -0.996546,1.083045 l -4.006908,0.333908 A 0.92013337,0.92013337 42.618189 0 1 44.000001,46.999999 V 33 a 0.92013291,0.92013291 137.38183 0 1 0.996546,-0.916954 l 4.006908,0.333909 a 1.0867999,1.0867999 47.381826 0 1 0.996546,1.083046 z" | ||||||
|  |          id="path3" | ||||||
|  |          inkscape:path-effect="#path-effect3" | ||||||
|  |          inkscape:original-d="m 50.000001,32.500001 v 15 l -6,0.499998 V 32 Z" | ||||||
|  |          sodipodi:nodetypes="ccccc" | ||||||
|  |          inkscape:label="windshield" /> | ||||||
|  |       <path | ||||||
|  |          style="fill:#af3116;fill-opacity:1;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |          d="m 29.000001,46.999999 c -4,0 -8.000001,0 -11.000001,-1 v -12 c 3,-1 7.000001,-1 11.000001,-1 z" | ||||||
|  |          id="path4" | ||||||
|  |          sodipodi:nodetypes="ccccc" | ||||||
|  |          inkscape:label="back_window" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 5.2 KiB | 
							
								
								
									
										202
									
								
								logo.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								logo.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||||
|  | <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||||
|  |  | ||||||
|  | <svg | ||||||
|  |    width="64" | ||||||
|  |    height="64" | ||||||
|  |    viewBox="0 0 64 64.000003" | ||||||
|  |    version="1.1" | ||||||
|  |    id="svg1" | ||||||
|  |    inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)" | ||||||
|  |    sodipodi:docname="logo.svg" | ||||||
|  |    inkscape:export-filename="logo.png" | ||||||
|  |    inkscape:export-xdpi="768" | ||||||
|  |    inkscape:export-ydpi="768" | ||||||
|  |    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||||
|  |    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||||
|  |    xmlns="http://www.w3.org/2000/svg" | ||||||
|  |    xmlns:svg="http://www.w3.org/2000/svg"> | ||||||
|  |   <sodipodi:namedview | ||||||
|  |      id="namedview1" | ||||||
|  |      pagecolor="#ffffff" | ||||||
|  |      bordercolor="#000000" | ||||||
|  |      borderopacity="0.25" | ||||||
|  |      inkscape:showpageshadow="2" | ||||||
|  |      inkscape:pageopacity="0.0" | ||||||
|  |      inkscape:pagecheckerboard="0" | ||||||
|  |      inkscape:deskcolor="#d1d1d1" | ||||||
|  |      inkscape:document-units="mm" | ||||||
|  |      showgrid="true" | ||||||
|  |      inkscape:zoom="11.313709" | ||||||
|  |      inkscape:cx="31.952386" | ||||||
|  |      inkscape:cy="30.803338" | ||||||
|  |      inkscape:window-width="1920" | ||||||
|  |      inkscape:window-height="1016" | ||||||
|  |      inkscape:window-x="0" | ||||||
|  |      inkscape:window-y="27" | ||||||
|  |      inkscape:window-maximized="1" | ||||||
|  |      inkscape:current-layer="layer1" | ||||||
|  |      inkscape:export-bgcolor="#ffffffff"> | ||||||
|  |     <inkscape:grid | ||||||
|  |        id="grid1" | ||||||
|  |        units="px" | ||||||
|  |        originx="0" | ||||||
|  |        originy="0" | ||||||
|  |        spacingx="1" | ||||||
|  |        spacingy="1" | ||||||
|  |        empcolor="#0099e5" | ||||||
|  |        empopacity="0.30196078" | ||||||
|  |        color="#0099e5" | ||||||
|  |        opacity="0.14901961" | ||||||
|  |        empspacing="8" | ||||||
|  |        enabled="true" | ||||||
|  |        visible="true" /> | ||||||
|  |   </sodipodi:namedview> | ||||||
|  |   <defs | ||||||
|  |      id="defs1"> | ||||||
|  |     <inkscape:path-effect | ||||||
|  |        effect="fillet_chamfer" | ||||||
|  |        id="path-effect3" | ||||||
|  |        is_visible="true" | ||||||
|  |        lpeversion="1" | ||||||
|  |        nodesatellites_param="F,0,0,1,0,1.0000002,0,1 @ F,0,1,1,0,1.0000002,0,1 @ F,0,1,1,0,1.0000002,0,1 @ F,0,1,1,0,1.0000002,0,1" | ||||||
|  |        radius="0" | ||||||
|  |        unit="px" | ||||||
|  |        method="auto" | ||||||
|  |        mode="F" | ||||||
|  |        chamfer_steps="1" | ||||||
|  |        flexible="false" | ||||||
|  |        use_knot_distance="true" | ||||||
|  |        apply_no_radius="true" | ||||||
|  |        apply_with_radius="true" | ||||||
|  |        only_selected="false" | ||||||
|  |        hide_knots="false" /> | ||||||
|  |     <inkscape:path-effect | ||||||
|  |        effect="fillet_chamfer" | ||||||
|  |        id="path-effect2" | ||||||
|  |        is_visible="true" | ||||||
|  |        lpeversion="1" | ||||||
|  |        nodesatellites_param="F,0,1,1,0,1.0000001,0,1 @ F,0,1,1,0,1.0000001,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,1,1,0,1.0000001,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1" | ||||||
|  |        radius="0" | ||||||
|  |        unit="px" | ||||||
|  |        method="auto" | ||||||
|  |        mode="F" | ||||||
|  |        chamfer_steps="1" | ||||||
|  |        flexible="false" | ||||||
|  |        use_knot_distance="true" | ||||||
|  |        apply_no_radius="true" | ||||||
|  |        apply_with_radius="true" | ||||||
|  |        only_selected="false" | ||||||
|  |        hide_knots="false" /> | ||||||
|  |   </defs> | ||||||
|  |   <g | ||||||
|  |      inkscape:label="Calque 1" | ||||||
|  |      inkscape:groupmode="layer" | ||||||
|  |      id="layer1"> | ||||||
|  |     <g | ||||||
|  |        id="g14" | ||||||
|  |        transform="matrix(1.12,0,0,1.12,-6.6400002,3.1600025)"> | ||||||
|  |       <path | ||||||
|  |          id="path13" | ||||||
|  |          style="fill:none;fill-opacity:1;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" | ||||||
|  |          d="m 38,43.499998 -11,-21 m 0,0 -9,20 M 50,32.749996 38,43.499998 M 53,16.749997 50,32.749996 M 40.000001,7.9999998 27,22.499998 M 16,7.9999998 27,22.499998" | ||||||
|  |          sodipodi:nodetypes="cccccccccccc" /> | ||||||
|  |       <g | ||||||
|  |          id="g13"> | ||||||
|  |         <circle | ||||||
|  |            style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" | ||||||
|  |            id="path5" | ||||||
|  |            cx="16" | ||||||
|  |            cy="8" | ||||||
|  |            r="3" /> | ||||||
|  |         <circle | ||||||
|  |            style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" | ||||||
|  |            id="path5-3" | ||||||
|  |            cx="40" | ||||||
|  |            cy="7.9999995" | ||||||
|  |            r="3" /> | ||||||
|  |         <circle | ||||||
|  |            style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" | ||||||
|  |            id="path5-1" | ||||||
|  |            cx="27" | ||||||
|  |            cy="22.499998" | ||||||
|  |            r="3" /> | ||||||
|  |         <circle | ||||||
|  |            style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" | ||||||
|  |            id="path5-6" | ||||||
|  |            cx="38" | ||||||
|  |            cy="43.499996" | ||||||
|  |            r="3" /> | ||||||
|  |         <circle | ||||||
|  |            style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" | ||||||
|  |            id="path5-18" | ||||||
|  |            cx="18" | ||||||
|  |            cy="42.499996" | ||||||
|  |            r="3" /> | ||||||
|  |         <circle | ||||||
|  |            style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" | ||||||
|  |            id="path5-2" | ||||||
|  |            cx="50" | ||||||
|  |            cy="32.749996" | ||||||
|  |            r="3" /> | ||||||
|  |         <circle | ||||||
|  |            style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" | ||||||
|  |            id="path5-22" | ||||||
|  |            cx="53" | ||||||
|  |            cy="16.749996" | ||||||
|  |            r="3" /> | ||||||
|  |       </g> | ||||||
|  |       <g | ||||||
|  |          id="g5" | ||||||
|  |          inkscape:label="car" | ||||||
|  |          transform="matrix(0.1163734,0.24290774,-0.24290774,0.1163734,38.296019,19.616451)" | ||||||
|  |          style="display:inline"> | ||||||
|  |         <g | ||||||
|  |            id="g4" | ||||||
|  |            inkscape:label="wheels"> | ||||||
|  |           <path | ||||||
|  |              style="fill:#000000;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |              d="m 25.000001,31.999999 v -2 h 4 v 2 z" | ||||||
|  |              id="path2" | ||||||
|  |              sodipodi:nodetypes="ccccc" /> | ||||||
|  |           <path | ||||||
|  |              style="fill:#000000;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |              d="m 43.000001,31.999999 v -2 h 4 v 2 z" | ||||||
|  |              id="path2-3" | ||||||
|  |              sodipodi:nodetypes="ccccc" /> | ||||||
|  |           <path | ||||||
|  |              style="fill:#000000;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |              d="m 25.000001,49.999999 v -2 h 4 v 2 z" | ||||||
|  |              id="path2-1" | ||||||
|  |              sodipodi:nodetypes="ccccc" /> | ||||||
|  |           <path | ||||||
|  |              style="fill:#000000;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |              d="m 43.000001,49.999999 v -2 h 4 v 2 z" | ||||||
|  |              id="path2-3-2" | ||||||
|  |              sodipodi:nodetypes="ccccc" /> | ||||||
|  |         </g> | ||||||
|  |         <path | ||||||
|  |            style="fill:#e14324;fill-opacity:1;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |            d="m 16,33 v 13.999999 a 1.1327823,1.1327823 48.562508 0 0 0.992278,1.124035 l 7.007723,0.875965 h 24 l 11.003454,-0.916955 a 1.0867997,1.0867997 132.61818 0 0 0.996546,-1.083045 v -14 A 1.0867996,1.0867996 47.381818 0 0 59.003455,31.916954 L 48.000001,31 h -24 l -7.007723,0.875965 A 1.1327823,1.1327823 131.43749 0 0 16,33 Z" | ||||||
|  |            id="path1" | ||||||
|  |            inkscape:path-effect="#path-effect2" | ||||||
|  |            inkscape:original-d="m 16,32 v 15.999999 l 8.000001,1 h 24 l 12,-1 v -16 L 48.000001,31 h -24 z" | ||||||
|  |            inkscape:label="body" /> | ||||||
|  |         <path | ||||||
|  |            style="fill:#53170b;fill-opacity:1;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |            d="m 50.000001,33.500001 v 13 a 1.0867994,1.0867994 132.61819 0 1 -0.996546,1.083045 l -4.006908,0.333908 A 0.92013337,0.92013337 42.618189 0 1 44.000001,46.999999 V 33 a 0.92013291,0.92013291 137.38183 0 1 0.996546,-0.916954 l 4.006908,0.333909 a 1.0867999,1.0867999 47.381826 0 1 0.996546,1.083046 z" | ||||||
|  |            id="path3" | ||||||
|  |            inkscape:path-effect="#path-effect3" | ||||||
|  |            inkscape:original-d="m 50.000001,32.500001 v 15 l -6,0.499998 V 32 Z" | ||||||
|  |            sodipodi:nodetypes="ccccc" | ||||||
|  |            inkscape:label="windshield" /> | ||||||
|  |         <path | ||||||
|  |            style="fill:#af3116;fill-opacity:1;stroke-linecap:round;stroke-linejoin:round" | ||||||
|  |            d="m 29.000001,46.999999 c -4,0 -8.000001,0 -11.000001,-1 v -12 c 3,-1 7.000001,-1 11.000001,-1 z" | ||||||
|  |            id="path4" | ||||||
|  |            sodipodi:nodetypes="ccccc" | ||||||
|  |            inkscape:label="back_window" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 7.5 KiB | 
							
								
								
									
										12
									
								
								src/car.py
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/car.py
									
									
									
									
									
								
							| @@ -1,7 +1,5 @@ | |||||||
| from __future__ import annotations |  | ||||||
|  |  | ||||||
| from math import radians | from math import radians | ||||||
| from typing import TYPE_CHECKING, Optional | from typing import Optional | ||||||
|  |  | ||||||
| import pygame | import pygame | ||||||
|  |  | ||||||
| @@ -10,9 +8,6 @@ from src.remote_controller import RemoteController | |||||||
| from src.utils import get_segments_intersection, segments_intersect | from src.utils import get_segments_intersection, segments_intersect | ||||||
| from src.vec import Vec | from src.vec import Vec | ||||||
|  |  | ||||||
| if TYPE_CHECKING: |  | ||||||
|     from src.game import Game |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def sign(x): return 0 if x == 0 else (-1 if x < 0 else 1) | def sign(x): return 0 if x == 0 else (-1 if x < 0 else 1) | ||||||
|  |  | ||||||
| @@ -32,8 +27,7 @@ class Car: | |||||||
|     RAYS_FOV = 180 |     RAYS_FOV = 180 | ||||||
|     RAYS_MAX_DIST = 100 |     RAYS_MAX_DIST = 100 | ||||||
|  |  | ||||||
|     def __init__(self, game: Game, pos: Vec, direction: Vec) -> None: |     def __init__(self, pos: Vec, direction: Vec) -> None: | ||||||
|         self.game: Game = game |  | ||||||
|         self.initial_pos: Vec = pos.copy() |         self.initial_pos: Vec = pos.copy() | ||||||
|         self.initial_dir: Vec = direction.copy() |         self.initial_dir: Vec = direction.copy() | ||||||
|         self.pos: Vec = pos |         self.pos: Vec = pos | ||||||
| @@ -48,7 +42,7 @@ class Car: | |||||||
|         self.rays: list[float] = [0] * self.N_RAYS |         self.rays: list[float] = [0] * self.N_RAYS | ||||||
|         self.rays_end: list[Vec] = [Vec() for _ in range(self.N_RAYS)] |         self.rays_end: list[Vec] = [Vec() for _ in range(self.N_RAYS)] | ||||||
|  |  | ||||||
|         self.controller: RemoteController = RemoteController(self.game, self) |         self.controller: RemoteController = RemoteController(self) | ||||||
|         self.controller.start_server() |         self.controller.start_server() | ||||||
|  |  | ||||||
|     def update(self, dt: float): |     def update(self, dt: float): | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								src/game.py
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/game.py
									
									
									
									
									
								
							| @@ -19,11 +19,10 @@ class Game: | |||||||
|         self.win: pygame.Surface = pygame.display.set_mode( |         self.win: pygame.Surface = pygame.display.set_mode( | ||||||
|             self.DEFAULT_SIZE, pygame.RESIZABLE |             self.DEFAULT_SIZE, pygame.RESIZABLE | ||||||
|         ) |         ) | ||||||
|         self.game_surf: pygame.Surface = pygame.Surface(self.DEFAULT_SIZE) |  | ||||||
|         pygame.display.set_caption("Rally Racer") |         pygame.display.set_caption("Rally Racer") | ||||||
|         self.running: bool = True |         self.running: bool = True | ||||||
|         self.track: Track = Track.load("simple") |         self.track: Track = Track.load("simple") | ||||||
|         self.car: Car = Car(self, self.track.start_pos, self.track.start_dir) |         self.car: Car = Car(self.track.start_pos, self.track.start_dir) | ||||||
|         self.camera: Camera = Camera() |         self.camera: Camera = Camera() | ||||||
|  |  | ||||||
|         self.clock: pygame.time.Clock = pygame.time.Clock() |         self.clock: pygame.time.Clock = pygame.time.Clock() | ||||||
| @@ -50,7 +49,6 @@ class Game: | |||||||
|             if event.type == pygame.QUIT: |             if event.type == pygame.QUIT: | ||||||
|                 self.quit() |                 self.quit() | ||||||
|             elif event.type == pygame.VIDEORESIZE: |             elif event.type == pygame.VIDEORESIZE: | ||||||
|                 self.game_surf = pygame.Surface((event.w, event.h)) |  | ||||||
|                 self.camera.set_size(Vec(event.w, event.h)) |                 self.camera.set_size(Vec(event.w, event.h)) | ||||||
|             elif event.type == pygame.KEYDOWN: |             elif event.type == pygame.KEYDOWN: | ||||||
|                 if event.key == pygame.K_ESCAPE: |                 if event.key == pygame.K_ESCAPE: | ||||||
| @@ -70,10 +68,9 @@ class Game: | |||||||
|         self.car.controller.close() |         self.car.controller.close() | ||||||
|  |  | ||||||
|     def render(self): |     def render(self): | ||||||
|         self.game_surf.fill(self.BACKGROUND_COLOR) |         self.win.fill(self.BACKGROUND_COLOR) | ||||||
|         self.track.render(self.game_surf, self.camera) |         self.track.render(self.win, self.camera) | ||||||
|         self.car.render(self.game_surf, self.camera, self.show_raycasts) |         self.car.render(self.win, self.camera, self.show_raycasts) | ||||||
|         self.win.blit(self.game_surf, (0, 0)) |  | ||||||
|         if self.show_fps: |         if self.show_fps: | ||||||
|             self.render_fps() |             self.render_fps() | ||||||
|         if self.show_speed: |         if self.show_speed: | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | import lzma | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| import struct | import struct | ||||||
| import time | import time | ||||||
| @@ -12,7 +13,7 @@ class RecordFile: | |||||||
|     def __init__(self, path: str | Path, mode: Literal["w", "r"]) -> None: |     def __init__(self, path: str | Path, mode: Literal["w", "r"]) -> None: | ||||||
|         self.path: str | Path = path |         self.path: str | Path = path | ||||||
|         self.mode: Literal["w", "r"] = mode |         self.mode: Literal["w", "r"] = mode | ||||||
|         self.file = open(self.path, self.mode + "b") |         self.file: lzma.LZMAFile = lzma.LZMAFile(self.path, self.mode) | ||||||
|  |  | ||||||
|     def __enter__(self): |     def __enter__(self): | ||||||
|         return self |         return self | ||||||
| @@ -21,8 +22,7 @@ class RecordFile: | |||||||
|         self.file.close() |         self.file.close() | ||||||
|  |  | ||||||
|     def write_header(self, n_snapshots: int): |     def write_header(self, n_snapshots: int): | ||||||
|         data: bytes = struct.pack( |         data: bytes = struct.pack(">IId", self.VERSION, n_snapshots, time.time()) | ||||||
|             ">IId", self.VERSION, n_snapshots, time.time()) |  | ||||||
|         self.file.write(data) |         self.file.write(data) | ||||||
|  |  | ||||||
|     def write_snapshots(self, snapshots: list[Snapshot]): |     def write_snapshots(self, snapshots: list[Snapshot]): | ||||||
| @@ -35,7 +35,8 @@ class RecordFile: | |||||||
|         version: int = struct.unpack(">I", self.file.read(4))[0] |         version: int = struct.unpack(">I", self.file.read(4))[0] | ||||||
|         if version != self.VERSION: |         if version != self.VERSION: | ||||||
|             raise ValueError( |             raise ValueError( | ||||||
|                 f"Cannot parse record file with format version {version} (current version: {self.VERSION})") |                 f"Cannot parse record file with format version {version} (current version: {self.VERSION})" | ||||||
|  |             ) | ||||||
|  |  | ||||||
|         n_snapshots: int |         n_snapshots: int | ||||||
|         timestamp: float |         timestamp: float | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ from src.snapshot import Snapshot | |||||||
|  |  | ||||||
|  |  | ||||||
| class RecorderClient(QObject): | class RecorderClient(QObject): | ||||||
|     DATA_CHUNK_SIZE = 65536 |     DATA_CHUNK_SIZE = 4096 | ||||||
|     data_received: pyqtSignal = pyqtSignal(Snapshot) |     data_received: pyqtSignal = pyqtSignal(Snapshot) | ||||||
|  |  | ||||||
|     def __init__(self, host: str, port: int) -> None: |     def __init__(self, host: str, port: int) -> None: | ||||||
| @@ -243,7 +243,7 @@ class RecorderWindow(Ui_Recorder, QMainWindow): | |||||||
|  |  | ||||||
|         self.SAVE_DIR.mkdir(exist_ok=True) |         self.SAVE_DIR.mkdir(exist_ok=True) | ||||||
|  |  | ||||||
|         record_name: str = "record_%d.rec" |         record_name: str = "record_%d.rec.xz" | ||||||
|         fid = 0 |         fid = 0 | ||||||
|         while os.path.exists(self.SAVE_DIR / (record_name % fid)): |         while os.path.exists(self.SAVE_DIR / (record_name % fid)): | ||||||
|             fid += 1 |             fid += 1 | ||||||
|   | |||||||
| @@ -12,12 +12,11 @@ from src.utils import RepeatTimer | |||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from src.car import Car |     from src.car import Car | ||||||
|     from src.game import Game |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class RemoteController: | class RemoteController: | ||||||
|     DEFAULT_PORT = 5000 |     DEFAULT_PORT = 5000 | ||||||
|     DATA_CHUNK_SIZE = 65536 |     DATA_CHUNK_SIZE = 4096 | ||||||
|  |  | ||||||
|     CONTROL_ATTRIBUTES: dict[CarControl, str] = { |     CONTROL_ATTRIBUTES: dict[CarControl, str] = { | ||||||
|         CarControl.FORWARD: "forward", |         CarControl.FORWARD: "forward", | ||||||
| @@ -28,8 +27,7 @@ class RemoteController: | |||||||
|  |  | ||||||
|     SNAPSHOT_INTERVAL = 0.1 |     SNAPSHOT_INTERVAL = 0.1 | ||||||
|  |  | ||||||
|     def __init__(self, game: Game, car: Car, port: int = DEFAULT_PORT) -> None: |     def __init__(self, car: Car, port: int = DEFAULT_PORT) -> None: | ||||||
|         self.game: Game = game |  | ||||||
|         self.car: Car = car |         self.car: Car = car | ||||||
|         self.port: int = port |         self.port: int = port | ||||||
|         self.server: socket.socket = socket.socket( |         self.server: socket.socket = socket.socket( | ||||||
| @@ -136,6 +134,5 @@ class RemoteController: | |||||||
|             return |             return | ||||||
|  |  | ||||||
|         snapshot: Snapshot = Snapshot.from_car(self.car) |         snapshot: Snapshot = Snapshot.from_car(self.car) | ||||||
|         snapshot.add_image(self.game) |  | ||||||
|         payload: bytes = snapshot.pack() |         payload: bytes = snapshot.pack() | ||||||
|         self.client.sendall(struct.pack(">I", len(payload)) + payload) |         self.client.sendall(struct.pack(">I", len(payload)) + payload) | ||||||
|   | |||||||
| @@ -5,13 +5,11 @@ from dataclasses import dataclass, field | |||||||
| from typing import TYPE_CHECKING, Optional | from typing import TYPE_CHECKING, Optional | ||||||
|  |  | ||||||
| import numpy as np | import numpy as np | ||||||
| import pygame |  | ||||||
|  |  | ||||||
| from src.vec import Vec | from src.vec import Vec | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from src.car import Car |     from src.car import Car | ||||||
|     from src.game import Game |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def iter_unpack(format, data): | def iter_unpack(format, data): | ||||||
| @@ -101,6 +99,3 @@ class Snapshot: | |||||||
|         car.pos = self.position.copy() |         car.pos = self.position.copy() | ||||||
|         car.direction = self.direction.copy() |         car.direction = self.direction.copy() | ||||||
|         car.speed = 0 |         car.speed = 0 | ||||||
|  |  | ||||||
|     def add_image(self, game: Game): |  | ||||||
|         self.image = pygame.surfarray.array3d(game.game_surf) |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user