Compare commits
	
		
			4 Commits
		
	
	
		
			d46aa6af45
			...
			cd866cc511
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cd866cc511 | |||
| 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,5 +1,7 @@ | ||||
| from __future__ import annotations | ||||
|  | ||||
| from math import radians | ||||
| from typing import Optional | ||||
| from typing import TYPE_CHECKING, Optional | ||||
|  | ||||
| import pygame | ||||
|  | ||||
| @@ -8,6 +10,9 @@ from src.remote_controller import RemoteController | ||||
| from src.utils import get_segments_intersection, segments_intersect | ||||
| 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) | ||||
|  | ||||
| @@ -27,7 +32,8 @@ class Car: | ||||
|     RAYS_FOV = 180 | ||||
|     RAYS_MAX_DIST = 100 | ||||
|  | ||||
|     def __init__(self, pos: Vec, direction: Vec) -> None: | ||||
|     def __init__(self, game: Game, pos: Vec, direction: Vec) -> None: | ||||
|         self.game: Game = game | ||||
|         self.initial_pos: Vec = pos.copy() | ||||
|         self.initial_dir: Vec = direction.copy() | ||||
|         self.pos: Vec = pos | ||||
| @@ -42,7 +48,7 @@ class Car: | ||||
|         self.rays: list[float] = [0] * self.N_RAYS | ||||
|         self.rays_end: list[Vec] = [Vec() for _ in range(self.N_RAYS)] | ||||
|  | ||||
|         self.controller: RemoteController = RemoteController(self) | ||||
|         self.controller: RemoteController = RemoteController(self.game, self) | ||||
|         self.controller.start_server() | ||||
|  | ||||
|     def update(self, dt: float): | ||||
|   | ||||
							
								
								
									
										11
									
								
								src/game.py
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/game.py
									
									
									
									
									
								
							| @@ -19,10 +19,11 @@ class Game: | ||||
|         self.win: pygame.Surface = pygame.display.set_mode( | ||||
|             self.DEFAULT_SIZE, pygame.RESIZABLE | ||||
|         ) | ||||
|         self.game_surf: pygame.Surface = pygame.Surface(self.DEFAULT_SIZE) | ||||
|         pygame.display.set_caption("Rally Racer") | ||||
|         self.running: bool = True | ||||
|         self.track: Track = Track.load("simple") | ||||
|         self.car: Car = Car(self.track.start_pos, self.track.start_dir) | ||||
|         self.car: Car = Car(self, self.track.start_pos, self.track.start_dir) | ||||
|         self.camera: Camera = Camera() | ||||
|  | ||||
|         self.clock: pygame.time.Clock = pygame.time.Clock() | ||||
| @@ -49,6 +50,7 @@ class Game: | ||||
|             if event.type == pygame.QUIT: | ||||
|                 self.quit() | ||||
|             elif event.type == pygame.VIDEORESIZE: | ||||
|                 self.game_surf = pygame.Surface((event.w, event.h)) | ||||
|                 self.camera.set_size(Vec(event.w, event.h)) | ||||
|             elif event.type == pygame.KEYDOWN: | ||||
|                 if event.key == pygame.K_ESCAPE: | ||||
| @@ -68,9 +70,10 @@ class Game: | ||||
|         self.car.controller.close() | ||||
|  | ||||
|     def render(self): | ||||
|         self.win.fill(self.BACKGROUND_COLOR) | ||||
|         self.track.render(self.win, self.camera) | ||||
|         self.car.render(self.win, self.camera, self.show_raycasts) | ||||
|         self.game_surf.fill(self.BACKGROUND_COLOR) | ||||
|         self.track.render(self.game_surf, self.camera) | ||||
|         self.car.render(self.game_surf, self.camera, self.show_raycasts) | ||||
|         self.win.blit(self.game_surf, (0, 0)) | ||||
|         if self.show_fps: | ||||
|             self.render_fps() | ||||
|         if self.show_speed: | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| import lzma | ||||
| from pathlib import Path | ||||
| import struct | ||||
| import time | ||||
| @@ -12,7 +13,7 @@ class RecordFile: | ||||
|     def __init__(self, path: str | Path, mode: Literal["w", "r"]) -> None: | ||||
|         self.path: str | Path = path | ||||
|         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): | ||||
|         return self | ||||
| @@ -21,8 +22,7 @@ class RecordFile: | ||||
|         self.file.close() | ||||
|  | ||||
|     def write_header(self, n_snapshots: int): | ||||
|         data: bytes = struct.pack( | ||||
|             ">IId", self.VERSION, n_snapshots, time.time()) | ||||
|         data: bytes = struct.pack(">IId", self.VERSION, n_snapshots, time.time()) | ||||
|         self.file.write(data) | ||||
|  | ||||
|     def write_snapshots(self, snapshots: list[Snapshot]): | ||||
| @@ -35,7 +35,8 @@ class RecordFile: | ||||
|         version: int = struct.unpack(">I", self.file.read(4))[0] | ||||
|         if version != self.VERSION: | ||||
|             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 | ||||
|         timestamp: float | ||||
|   | ||||
| @@ -17,7 +17,7 @@ from src.snapshot import Snapshot | ||||
|  | ||||
|  | ||||
| class RecorderClient(QObject): | ||||
|     DATA_CHUNK_SIZE = 4096 | ||||
|     DATA_CHUNK_SIZE = 65536 | ||||
|     data_received: pyqtSignal = pyqtSignal(Snapshot) | ||||
|  | ||||
|     def __init__(self, host: str, port: int) -> None: | ||||
| @@ -243,7 +243,7 @@ class RecorderWindow(Ui_Recorder, QMainWindow): | ||||
|  | ||||
|         self.SAVE_DIR.mkdir(exist_ok=True) | ||||
|  | ||||
|         record_name: str = "record_%d.rec" | ||||
|         record_name: str = "record_%d.rec.xz" | ||||
|         fid = 0 | ||||
|         while os.path.exists(self.SAVE_DIR / (record_name % fid)): | ||||
|             fid += 1 | ||||
|   | ||||
| @@ -12,11 +12,12 @@ from src.utils import RepeatTimer | ||||
|  | ||||
| if TYPE_CHECKING: | ||||
|     from src.car import Car | ||||
|     from src.game import Game | ||||
|  | ||||
|  | ||||
| class RemoteController: | ||||
|     DEFAULT_PORT = 5000 | ||||
|     DATA_CHUNK_SIZE = 4096 | ||||
|     DATA_CHUNK_SIZE = 65536 | ||||
|  | ||||
|     CONTROL_ATTRIBUTES: dict[CarControl, str] = { | ||||
|         CarControl.FORWARD: "forward", | ||||
| @@ -27,7 +28,8 @@ class RemoteController: | ||||
|  | ||||
|     SNAPSHOT_INTERVAL = 0.1 | ||||
|  | ||||
|     def __init__(self, car: Car, port: int = DEFAULT_PORT) -> None: | ||||
|     def __init__(self, game: Game, car: Car, port: int = DEFAULT_PORT) -> None: | ||||
|         self.game: Game = game | ||||
|         self.car: Car = car | ||||
|         self.port: int = port | ||||
|         self.server: socket.socket = socket.socket( | ||||
| @@ -134,5 +136,6 @@ class RemoteController: | ||||
|             return | ||||
|  | ||||
|         snapshot: Snapshot = Snapshot.from_car(self.car) | ||||
|         snapshot.add_image(self.game) | ||||
|         payload: bytes = snapshot.pack() | ||||
|         self.client.sendall(struct.pack(">I", len(payload)) + payload) | ||||
|   | ||||
| @@ -5,11 +5,13 @@ from dataclasses import dataclass, field | ||||
| from typing import TYPE_CHECKING, Optional | ||||
|  | ||||
| import numpy as np | ||||
| import pygame | ||||
|  | ||||
| from src.vec import Vec | ||||
|  | ||||
| if TYPE_CHECKING: | ||||
|     from src.car import Car | ||||
|     from src.game import Game | ||||
|  | ||||
|  | ||||
| def iter_unpack(format, data): | ||||
| @@ -99,3 +101,6 @@ class Snapshot: | ||||
|         car.pos = self.position.copy() | ||||
|         car.direction = self.direction.copy() | ||||
|         car.speed = 0 | ||||
|  | ||||
|     def add_image(self, game: Game): | ||||
|         self.image = pygame.surfarray.array3d(game.game_surf) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user