diff --git a/Battle Pong/Ball.gd b/Battle Pong/Ball.gd index f459c43484c607c30ddb6002a40460780e1aa48f..ae707220fd4e87e478dd453a9e4e945c1ced3272 100644 --- a/Battle Pong/Ball.gd +++ b/Battle Pong/Ball.gd @@ -5,6 +5,7 @@ extends KinematicBody2D export var speed = 300 export var speed_max = 600 export var bounce_degree_max = 60 +export var speed_increment = 20 var ball_size var playing = false var velocity = Vector2(100,0) @@ -15,6 +16,9 @@ var y_range = 100 # Called when the node enters the scene tree for the first time. func _ready(): set_sync_to_physics(false) + speed = $"/root/GameSettings".ball_speed_min + speed_max = $"/root/GameSettings".ball_speed_max + speed_increment = $"/root/GameSettings".ball_speed_increment pass @@ -46,7 +50,7 @@ func run(delta): func ball_hit_paddle(player_position, player_shape, is_player_one): # Geschwindigkeitserhöhung if speed < speed_max: - speed +=20 + speed +=speed_increment # Ball geht in die andere Richtung velocity.x*=-1 @@ -91,4 +95,6 @@ func start(pos): func get_observation(): var move = velocity.normalized()*speed - return {"velocity":{"X":move.x,"Y":move.y}, "position":{"X":position.x,"Y":position.y}} + var ball_player_one = {"velocity":{"X":move.x,"Y":move.y}, "position":{"X":position.x,"Y":position.y}} + var ball_player_two = {"velocity":{"X":move.x*(-1),"Y":move.y}, "position":{"X":abs(position.x - $"/root/GameSettings".display_window_width),"Y":position.y}} + return {'player_one':ball_player_one, 'player_two':ball_player_two} diff --git a/Battle Pong/GameSettings.gd b/Battle Pong/GameSettings.gd index 4647f076c0f2c439d444431334d7120a494b3d49..a3e92d4cc6ade797867dcc1fec984402955d07db 100644 --- a/Battle Pong/GameSettings.gd +++ b/Battle Pong/GameSettings.gd @@ -3,13 +3,17 @@ extends Node export var rendering_enabled = false export var learn_with_images = true export var trainings_mode_enabled = false +export var local_two_player = false +# Constant values !!!! Keep always up to date !!! +var display_window_width = 1024 +var display_window_height = 600 var game_playtime_per_step var game_wins_to_reset var game_port -var image_format +var image_rgb var image_heigth var image_width @@ -39,7 +43,7 @@ var default_data = { "port":9080 }, "image":{ - "format":"RGB8", + "format":"L8", "height":60, "width":100 }, @@ -87,6 +91,10 @@ func load_data(): update_settings() func save_data(): + var format = "L8" + if $"/root/GameSettings".image_rgb: + format = "RGB8" + data = { "game":{ "playtime_per_step":$"/root/GameSettings".game_playtime_per_step, @@ -94,7 +102,7 @@ func save_data(): "port":$"/root/GameSettings".game_port }, "image":{ - "format":$"/root/GameSettings".image_format, + "format":format, "height":$"/root/GameSettings".image_heigth, "width":$"/root/GameSettings".image_width }, @@ -135,7 +143,11 @@ func update_settings(): game_wins_to_reset = data["game"]["wins_to_reset"] as int game_port= data["game"]["port"] as int - image_format = data["image"]["format"] + var format = data["image"]["format"] + if format =="RGB8": + image_rgb =true + else: + image_rgb = false; image_heigth = data["image"]["height"] as int image_width = data["image"]["width"] as int diff --git a/Battle Pong/GameSettings.tscn b/Battle Pong/GameSettings.tscn index efa62e8cca79ffae20ce8ca542669a1e814ae9bc..52a3e75e47aa5de45ef1dda259c337a6a5cc3f15 100644 --- a/Battle Pong/GameSettings.tscn +++ b/Battle Pong/GameSettings.tscn @@ -3,4 +3,5 @@ [ext_resource path="res://GameSettings.gd" type="Script" id=1] [node name="Node" type="Node"] +pause_mode = 2 script = ExtResource( 1 ) diff --git a/Battle Pong/Main.gd b/Battle Pong/Main.gd index 79510193be0a063cfde55970f4c07aefe6b59537..43e797548303d62fcb1520e0c74fc0dcacb69f4f 100644 --- a/Battle Pong/Main.gd +++ b/Battle Pong/Main.gd @@ -1,7 +1,7 @@ extends Node # The port we will listen to. -const PORT = 9080 +var port = 0 # Our WebSocketServer instance. var _server = WebSocketServer.new() var player_one_client_id @@ -43,6 +43,7 @@ func _ready(): learn_with_images = $"/root/GameSettings".learn_with_images game_playtime_per_step = $"/root/GameSettings".game_playtime_per_step max_wins = $"/root/GameSettings".game_wins_to_reset + port = $"/root/GameSettings".game_port if not enable_rendering: VisualServer.render_loop_enabled = false # disable rendering to create a massive boost if learn_with_images: @@ -50,21 +51,26 @@ func _ready(): $Player1Score.hide() $Player2Score.hide() - # Connect base signals to get notified of new client connections, - # disconnections, and disconnect requests. - _server.connect("client_connected", self, "_connected") - _server.connect("client_disconnected", self, "_disconnected") - _server.connect("client_close_request", self, "_close_request") - # This signal is emitted when not using the Multiplayer API every time a - # full packet is received. - # Alternatively, you could check get_peer(PEER_ID).get_available_packets() - # in a loop for each connected peer. - _server.connect("data_received", self, "_on_data") - # Start listening on the given port. - var server_err = _server.listen(PORT) - if server_err != OK: - print("Unable to start server") - set_process(false) + if( $"/root/GameSettings".local_two_player): + $LocalTwoPlayerTimer.wait_time = $"/root/GameSettings".game_playtime_per_step + $LocalTwoPlayerTimer.start() + + else: + # Connect base signals to get notified of new client connections, + # disconnections, and disconnect requests. + _server.connect("client_connected", self, "_connected") + _server.connect("client_disconnected", self, "_disconnected") + _server.connect("client_close_request", self, "_close_request") + # This signal is emitted when not using the Multiplayer API every time a + # full packet is received. + # Alternatively, you could check get_peer(PEER_ID).get_available_packets() + # in a loop for each connected peer. + _server.connect("data_received", self, "_on_data") + # Start listening on the given port. + var server_err = _server.listen(port) + if server_err != OK: + print("Unable to start server") + set_process(false) set_ball() $PlayerOne.start($StartPositionPlayerOne.position) @@ -248,11 +254,18 @@ func _on_data(id): play() var return_value = null if learn_with_images: - return_value = yield(get_yielded_return_value_as_utf8_JSON(), "completed") + return_value = yield(get_yielded_return_value(), "completed") else: - return_value = get_return_value_as_utf8_JSON() - _server.get_peer(id).put_packet(return_value) + return_value = get_return_value() + + if (player_one_client_id != null) and (player_one_client_id == id): + _server.get_peer(id).put_packet((JSON.print(return_value['player_one'])).to_utf8()) + elif player_two_client_id != null and player_two_client_id == id: + _server.get_peer(id).put_packet((JSON.print(return_value['player_two'])).to_utf8()) + else: + _server.get_peer(id).put_packet((JSON.print(return_value)).to_utf8()) # Fallback, if the id is unknown pass + if($"/root/GameSettings".trainings_mode_enabled): if(i=="player_one_training"): next_step_player_one = true @@ -278,7 +291,7 @@ func _on_data(id): action_trainer_two = "nothing" if(next_step_player_one and next_step_player_two): - if($"/root/GameSettings".trainer_realtime_enabled): + if($"/root/GameSettings".trainings_mode_enabled and $"/root/GameSettings".trainer_realtime_enabled): yield(get_tree().create_timer(game_playtime_per_step), "timeout") unpause() $PlayerOne.run(game_playtime_per_step) @@ -287,19 +300,25 @@ func _on_data(id): timeout() -func get_observarion(): - return {"PlayerOne":{"X":$PlayerOne.position.x,"Y":$PlayerOne.position.y, "TrainingAction":action_trainer_one}, "PlayerTwo":{"X":$PlayerTwo.position.x,"Y":$PlayerTwo.position.y, "TrainingAction":action_trainer_two}, "ball":ball.get_observation()} +func get_observation(): + var ball_observation=ball.get_observation() + var player_one = {"PlayerOne":{"X":$PlayerOne.position.x,"Y":$PlayerOne.position.y, "TrainingAction":action_trainer_one}, "PlayerTwo":{"X":$PlayerTwo.position.x,"Y":$PlayerTwo.position.y, "TrainingAction":action_trainer_two}, "ball":ball_observation['player_one']} + var player_two = {"PlayerOne":{"X":abs($PlayerTwo.position.x - $"/root/GameSettings".display_window_width),"Y":$PlayerTwo.position.y, "TrainingAction":action_trainer_two}, "PlayerTwo":{"X":abs($PlayerOne.position.x- $"/root/GameSettings".display_window_width),"Y":$PlayerOne.position.y, "TrainingAction":action_trainer_one}, "ball":ball_observation['player_two']} + return {"player_one":player_one, "player_two":player_two} -func get_return_value_as_utf8_JSON(): - var observation = get_observarion() - var return_value = {"observation":observation, "reward":{"PlayerOne":reward_player_one, "PlayerTwo":reward_player_two}, "done":(not playing), "info":{"PlayerOneScore":score_player_one, "PlayerTwoScore":score_player_two}} - return (JSON.print(return_value)).to_utf8() - -func get_yielded_return_value_as_utf8_JSON(): - var observation = get_observarion() +func get_return_value(): + var observation = get_observation() + var return_value_player_one = {"observation":observation['player_one'], "reward":{"PlayerOne":reward_player_one, "PlayerTwo":reward_player_two}, "done":(not playing), "info":{"PlayerOneScore":score_player_one, "PlayerTwoScore":score_player_two}} + var return_value_player_two = {"observation":observation['player_two'], "reward":{"PlayerOne":reward_player_two, "PlayerTwo":reward_player_one}, "done":(not playing), "info":{"PlayerOneScore":score_player_two, "PlayerTwoScore":score_player_one}} + return {'player_one':return_value_player_one, 'player_two':return_value_player_two} + +func get_yielded_return_value(): + var observation = get_observation() var screenshot = yield(get_screenshot(), "completed") - var return_value = {"observation":observation, "reward":{"PlayerOne":reward_player_one, "PlayerTwo":reward_player_two}, "done":(not playing), "info":{"PlayerOneScore":score_player_one, "PlayerTwoScore":score_player_two, "screenshot":screenshot}} - return (JSON.print(return_value)).to_utf8() + + var return_value_player_one = {"observation":observation['player_one'], "reward":{"PlayerOne":reward_player_one, "PlayerTwo":reward_player_two}, "done":(not playing), "info":{"PlayerOneScore":score_player_one, "PlayerTwoScore":score_player_two, "screenshot":screenshot['player_one']}} + var return_value_player_two = {"observation":observation['player_two'], "reward":{"PlayerOne":reward_player_two, "PlayerTwo":reward_player_one}, "done":(not playing), "info":{"PlayerOneScore":score_player_two, "PlayerTwoScore":score_player_one, "screenshot":screenshot['player_two']}} + return {'player_one':return_value_player_one, 'player_two':return_value_player_two} func get_screenshot(): get_viewport().set_clear_mode(Viewport.CLEAR_MODE_ONLY_NEXT_FRAME) @@ -309,21 +328,31 @@ func get_screenshot(): var thumbnail = get_viewport().get_texture().get_data() thumbnail.flip_y() thumbnail.resize(100, 60 ) - thumbnail.convert(Image.FORMAT_RGB8 ) # Farbe - #thumbnail.convert(Image.FORMAT_L8 ) # S/W + if $"/root/GameSettings".image_rgb: + thumbnail.convert(Image.FORMAT_RGB8 ) # Farbe + else: + thumbnail.convert(Image.FORMAT_L8 ) # S/W #thumbnail.save_png('test.png') # Save Image as file - to debug - var array : PoolByteArray = thumbnail.get_data() var sg_width = thumbnail.get_width() var sg_height = thumbnail.get_height() var sg_format = thumbnail.get_format() - var sg_u_size = array.size() - var sg_saved_img = Marshalls.raw_to_base64(array) - - var metadata = {"image" : sg_saved_img, "size" : sg_u_size, "width" : sg_width, "height" : sg_height, "format" : sg_format} - return metadata + var array_player_one : PoolByteArray = thumbnail.get_data() + var sg_saved_img_player_one = Marshalls.raw_to_base64(array_player_one) + var sg_u_size_player_one = array_player_one.size() + var screenshot_player_one = {"image" : sg_saved_img_player_one, "size" : sg_u_size_player_one, "width" : sg_width, "height" : sg_height, "format" : sg_format} + + thumbnail.flip_x() + var array_player_two : PoolByteArray = thumbnail.get_data() + var sg_saved_img_player_two = Marshalls.raw_to_base64(array_player_two) + var sg_u_size_player_two = array_player_two.size() + var screenshot_player_two = {"image" : sg_saved_img_player_one, "size" : sg_u_size_player_two, "width" : sg_width, "height" : sg_height, "format" : sg_format} + + + return {"player_one":screenshot_player_one, "player_two":screenshot_player_two} + func timeout(): pause() @@ -336,17 +365,17 @@ func timeout(): var return_value = null if learn_with_images: - return_value = yield(get_yielded_return_value_as_utf8_JSON(), "completed") + return_value = yield(get_yielded_return_value(), "completed") else: - return_value = get_return_value_as_utf8_JSON() + return_value = get_return_value() reward_player_one = 0 reward_player_two = 0 ##print(return_value) if(player_one_client_id): - _server.get_peer(player_one_client_id).put_packet(return_value) + _server.get_peer(player_one_client_id).put_packet((JSON.print(return_value['player_one'])).to_utf8()) if(player_two_client_id): - _server.get_peer(player_two_client_id).put_packet(return_value) + _server.get_peer(player_two_client_id).put_packet((JSON.print(return_value['player_two'])).to_utf8()) func pause(): @@ -360,3 +389,12 @@ func unpause(): ball.set_pause(false) $PlayerOne.set_pause(false) $PlayerTwo.set_pause(false) + + +func _on_LocalTwoPlayerTimer_timeout(): + unpause() + $PlayerOne.run(game_playtime_per_step) + $PlayerTwo.run(game_playtime_per_step) + ball.run(game_playtime_per_step) + timeout() + pass # Replace with function body. diff --git a/Battle Pong/Main.tscn b/Battle Pong/Main.tscn index 9240734d80fbbf9392d771bd3413eb7d67f4352e..1684f90fc939ea2a276a80a92abd2534c0fa8794 100644 --- a/Battle Pong/Main.tscn +++ b/Battle Pong/Main.tscn @@ -48,7 +48,7 @@ __meta__ = { } [node name="StartPositionPlayerTwo" type="Position2D" parent="."] -position = Vector2( 968, 300 ) +position = Vector2( 972, 300 ) [node name="StartPositionBall" type="Position2D" parent="."] position = Vector2( 512, 300 ) @@ -57,10 +57,8 @@ position = Vector2( 512, 300 ) position = Vector2( 52, 300 ) [node name="PlayerTwo" parent="." instance=ExtResource( 1 )] -pause_mode = 1 [node name="PlayerOne" parent="." instance=ExtResource( 1 )] -pause_mode = 1 is_player_one = true [node name="ColorRect4" type="ColorRect" parent="."] @@ -84,5 +82,9 @@ __meta__ = { [node name="WallBottom" parent="." instance=ExtResource( 3 )] position = Vector2( 0, 600 ) + +[node name="LocalTwoPlayerTimer" type="Timer" parent="."] +wait_time = 0.01 [connection signal="hit" from="PlayerTwo" to="." method="_on_PlayerTwo_hit"] [connection signal="hit" from="PlayerOne" to="." method="_on_PlayerOne_hit"] +[connection signal="timeout" from="LocalTwoPlayerTimer" to="." method="_on_LocalTwoPlayerTimer_timeout"] diff --git a/Battle Pong/MainMenu.gd b/Battle Pong/MainMenu.gd index d44016d220fa9325b963963a46e68c62aae7ae6a..15e891560a79f9d48f6bbd6c76835d7be67f975b 100644 --- a/Battle Pong/MainMenu.gd +++ b/Battle Pong/MainMenu.gd @@ -12,6 +12,10 @@ func _ready(): func _on_but_local_two_player_pressed(): + $"/root/GameSettings".rendering_enabled = true + $"/root/GameSettings".learn_with_images = false + $"/root/GameSettings".local_two_player = true; + get_tree().change_scene("res://Main.tscn") pass # Replace with function body. diff --git a/Battle Pong/MainMenu.tscn b/Battle Pong/MainMenu.tscn index 7ef8da7138ab32e18292725bc02266e405cbd1f9..3e316c4166b0da2f18552ea68b40dc80f74de779 100644 --- a/Battle Pong/MainMenu.tscn +++ b/Battle Pong/MainMenu.tscn @@ -3,6 +3,7 @@ [ext_resource path="res://MainMenu.gd" type="Script" id=1] [node name="MainMenu" type="Control"] +pause_mode = 2 anchor_right = 1.0 anchor_bottom = 1.0 script = ExtResource( 1 ) diff --git a/Battle Pong/Obstacle.tscn b/Battle Pong/Obstacle.tscn index c40423b847f4ce5a0e3c43022fbe9def3803c728..0db91a4ab8adb97cd5fc925662b894427c8106f4 100644 --- a/Battle Pong/Obstacle.tscn +++ b/Battle Pong/Obstacle.tscn @@ -4,6 +4,7 @@ extents = Vector2( 19.9777, 20.0466 ) [node name="Obstacle" type="StaticBody2D"] +pause_mode = 1 collision_layer = 4 collision_mask = 8 diff --git a/Battle Pong/Player.gd b/Battle Pong/Player.gd index 79029592e69ca378edaebc571454f3d4289ac999..2cd72e4f58c9fb6465516a26e78d83764cbb2ea4 100644 --- a/Battle Pong/Player.gd +++ b/Battle Pong/Player.gd @@ -12,6 +12,9 @@ func _ready(): player_size =$CollisionShape2D.shape.extents if(is_player_one): $ColorRect.color=Color(0,0,0,1) + speed = $"/root/GameSettings".player_one_speed + else: + speed = $"/root/GameSettings".player_two_speed diff --git a/Battle Pong/Player.tscn b/Battle Pong/Player.tscn index 7e7361b806ac08103e49ae10aee3db9532d27f83..39d728301e5a37f21e5f7fbea9e35eeb3533665a 100644 --- a/Battle Pong/Player.tscn +++ b/Battle Pong/Player.tscn @@ -6,6 +6,7 @@ extents = Vector2( 7.96318, 39.9475 ) [node name="Player" type="RigidBody2D"] +pause_mode = 1 position = Vector2( -0.325993, -1.00885 ) collision_layer = 2 collision_mask = 8 diff --git a/Battle Pong/SettingsWindow.gd b/Battle Pong/SettingsWindow.gd index fc400064a09ea0ab1b625ad597c89dfa41d21a38..cfebdf653e89c9b49ad22c1f9d37b17147c5c0a4 100644 --- a/Battle Pong/SettingsWindow.gd +++ b/Battle Pong/SettingsWindow.gd @@ -17,7 +17,7 @@ func update_gui(): $VBoxContainer/grid_settings/vbox_game_settings/grid_game_settings/input_game_wins_to_reset.text = str($"/root/GameSettings".game_wins_to_reset) $VBoxContainer/grid_settings/vbox_game_settings/grid_game_settings/input_game_port.text= str($"/root/GameSettings".game_port) - $VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings/input_image_format.text = $"/root/GameSettings".image_format + $VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings/input_image_format.pressed = $"/root/GameSettings".image_rgb $VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings/input_image_height.text = str($"/root/GameSettings".image_heigth) $VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings/input_image_width.text = str($"/root/GameSettings".image_width) @@ -57,7 +57,7 @@ func _on_but_save_pressed(): $"/root/GameSettings".game_wins_to_reset = $VBoxContainer/grid_settings/vbox_game_settings/grid_game_settings/input_game_wins_to_reset.text as int $"/root/GameSettings".game_port = $VBoxContainer/grid_settings/vbox_game_settings/grid_game_settings/input_game_port.text as int - $"/root/GameSettings".image_format = $VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings/input_image_format.text + $"/root/GameSettings".image_rgb = $VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings/input_image_format.pressed $"/root/GameSettings".image_heigth = $VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings/input_image_height.text as int $"/root/GameSettings".image_width = $VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings/input_image_width.text as int diff --git a/Battle Pong/SettingsWindow.tscn b/Battle Pong/SettingsWindow.tscn index c86b6595d7feefddc8e1a3a18f47013b0e56d0ed..51b5e09d3952e41d3bc2b3854d5c444fb2d0f7a2 100644 --- a/Battle Pong/SettingsWindow.tscn +++ b/Battle Pong/SettingsWindow.tscn @@ -3,6 +3,7 @@ [ext_resource path="res://SettingsWindow.gd" type="Script" id=1] [node name="Control" type="Control"] +pause_mode = 2 anchor_right = 1.0 anchor_bottom = 1.0 script = ExtResource( 1 ) @@ -37,7 +38,7 @@ __meta__ = { [node name="grid_settings" type="GridContainer" parent="VBoxContainer"] margin_right = 347.0 -margin_bottom = 353.0 +margin_bottom = 278.0 columns = 3 __meta__ = { "_edit_use_anchors_": false @@ -45,7 +46,7 @@ __meta__ = { [node name="vbox_game_settings" type="VBoxContainer" parent="VBoxContainer/grid_settings"] margin_right = 163.0 -margin_bottom = 105.0 +margin_bottom = 114.0 [node name="lbl_title_game_settings" type="Label" parent="VBoxContainer/grid_settings/vbox_game_settings"] margin_right = 163.0 @@ -97,12 +98,12 @@ margin_bottom = 87.0 [node name="VSeparator" type="VSeparator" parent="VBoxContainer/grid_settings"] margin_left = 167.0 margin_right = 171.0 -margin_bottom = 105.0 +margin_bottom = 114.0 [node name="vbox_image_settings" type="VBoxContainer" parent="VBoxContainer/grid_settings"] margin_left = 175.0 margin_right = 347.0 -margin_bottom = 105.0 +margin_bottom = 114.0 [node name="lbl_title_image_settings" type="Label" parent="VBoxContainer/grid_settings/vbox_image_settings"] margin_right = 172.0 @@ -112,69 +113,69 @@ text = "Image:" [node name="grid_image_settings" type="GridContainer" parent="VBoxContainer/grid_settings/vbox_image_settings"] margin_top = 18.0 margin_right = 172.0 -margin_bottom = 98.0 +margin_bottom = 114.0 columns = 2 [node name="lbl_image_format" type="Label" parent="VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings"] -margin_top = 5.0 -margin_right = 45.0 -margin_bottom = 19.0 -text = "Format" +margin_top = 13.0 +margin_right = 84.0 +margin_bottom = 27.0 +text = "Output RGB8" -[node name="input_image_format" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings"] -margin_left = 49.0 -margin_right = 107.0 -margin_bottom = 24.0 +[node name="input_image_format" type="CheckButton" parent="VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings"] +margin_left = 88.0 +margin_right = 164.0 +margin_bottom = 40.0 hint_tooltip = "example: RGB8 L8 https://docs.godotengine.org/en/stable/classes/class_image.html#enumerations" [node name="lbl_image_heigth" type="Label" parent="VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings"] -margin_top = 33.0 -margin_right = 45.0 -margin_bottom = 47.0 +margin_top = 49.0 +margin_right = 84.0 +margin_bottom = 63.0 text = "Height" [node name="input_image_height" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings"] -margin_left = 49.0 -margin_top = 28.0 -margin_right = 107.0 -margin_bottom = 52.0 +margin_left = 88.0 +margin_top = 44.0 +margin_right = 164.0 +margin_bottom = 68.0 [node name="lbl_image_width" type="Label" parent="VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings"] -margin_top = 61.0 -margin_right = 45.0 -margin_bottom = 75.0 +margin_top = 77.0 +margin_right = 84.0 +margin_bottom = 91.0 text = "Width" [node name="input_image_width" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_image_settings/grid_image_settings"] -margin_left = 49.0 -margin_top = 56.0 -margin_right = 107.0 -margin_bottom = 80.0 +margin_left = 88.0 +margin_top = 72.0 +margin_right = 164.0 +margin_bottom = 96.0 [node name="HSeparator" type="HSeparator" parent="VBoxContainer/grid_settings"] -margin_top = 109.0 +margin_top = 118.0 margin_right = 163.0 -margin_bottom = 113.0 +margin_bottom = 122.0 [node name="HSeparator6" type="HSeparator" parent="VBoxContainer/grid_settings"] margin_left = 167.0 -margin_top = 109.0 +margin_top = 118.0 margin_right = 171.0 -margin_bottom = 113.0 +margin_bottom = 122.0 [node name="HSeparator5" type="HSeparator" parent="VBoxContainer/grid_settings"] margin_left = 175.0 -margin_top = 109.0 +margin_top = 118.0 margin_right = 347.0 -margin_bottom = 113.0 +margin_bottom = 122.0 [node name="vbox_trainer_settings" type="VBoxContainer" parent="VBoxContainer/grid_settings"] -margin_top = 117.0 +margin_top = 126.0 margin_right = 163.0 -margin_bottom = 271.0 +margin_bottom = 224.0 [node name="lbl_title_trainer_settings" type="Label" parent="VBoxContainer/grid_settings/vbox_trainer_settings"] margin_right = 163.0 @@ -237,15 +238,15 @@ margin_bottom = 40.0 [node name="VSeparator2" type="VSeparator" parent="VBoxContainer/grid_settings"] margin_left = 167.0 -margin_top = 117.0 +margin_top = 126.0 margin_right = 171.0 -margin_bottom = 271.0 +margin_bottom = 224.0 [node name="vbox_ball_settings" type="VBoxContainer" parent="VBoxContainer/grid_settings"] margin_left = 175.0 -margin_top = 117.0 +margin_top = 126.0 margin_right = 347.0 -margin_bottom = 271.0 +margin_bottom = 224.0 [node name="lbl_title_ball_settings" type="Label" parent="VBoxContainer/grid_settings/vbox_ball_settings"] margin_right = 172.0 @@ -255,89 +256,89 @@ text = "Ball:" [node name="grid_ball_settings" type="GridContainer" parent="VBoxContainer/grid_settings/vbox_ball_settings"] margin_top = 18.0 margin_right = 172.0 -margin_bottom = 154.0 +margin_bottom = 98.0 columns = 2 [node name="lbl_ball_height" type="Label" parent="VBoxContainer/grid_settings/vbox_ball_settings/grid_ball_settings"] +visible = false margin_top = 5.0 margin_right = 110.0 margin_bottom = 19.0 text = "Hight" [node name="input_ball_height" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_ball_settings/grid_ball_settings"] -margin_left = 114.0 -margin_right = 172.0 +visible = false +margin_right = 58.0 margin_bottom = 24.0 [node name="lbl_ball_width" type="Label" parent="VBoxContainer/grid_settings/vbox_ball_settings/grid_ball_settings"] -margin_top = 33.0 +visible = false +margin_top = 5.0 margin_right = 110.0 -margin_bottom = 47.0 +margin_bottom = 19.0 text = "Width" [node name="input_ball_width" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_ball_settings/grid_ball_settings"] -margin_left = 114.0 -margin_top = 28.0 -margin_right = 172.0 -margin_bottom = 52.0 +visible = false +margin_right = 58.0 +margin_bottom = 24.0 [node name="lbl_ball_speed_min" type="Label" parent="VBoxContainer/grid_settings/vbox_ball_settings/grid_ball_settings"] -margin_top = 61.0 +margin_top = 5.0 margin_right = 110.0 -margin_bottom = 75.0 +margin_bottom = 19.0 text = "Speed Min" [node name="input_ball_speed_min" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_ball_settings/grid_ball_settings"] margin_left = 114.0 -margin_top = 56.0 margin_right = 172.0 -margin_bottom = 80.0 +margin_bottom = 24.0 [node name="lbl_ball_speed_max" type="Label" parent="VBoxContainer/grid_settings/vbox_ball_settings/grid_ball_settings"] -margin_top = 89.0 +margin_top = 33.0 margin_right = 110.0 -margin_bottom = 103.0 +margin_bottom = 47.0 text = "Speed Max" [node name="input_ball_speed_max" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_ball_settings/grid_ball_settings"] margin_left = 114.0 -margin_top = 84.0 +margin_top = 28.0 margin_right = 172.0 -margin_bottom = 108.0 +margin_bottom = 52.0 [node name="lbl_ball_speed_increment" type="Label" parent="VBoxContainer/grid_settings/vbox_ball_settings/grid_ball_settings"] -margin_top = 117.0 +margin_top = 61.0 margin_right = 110.0 -margin_bottom = 131.0 +margin_bottom = 75.0 text = "Speed Increment" [node name="input_ball_speed_increment" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_ball_settings/grid_ball_settings"] margin_left = 114.0 -margin_top = 112.0 +margin_top = 56.0 margin_right = 172.0 -margin_bottom = 136.0 +margin_bottom = 80.0 [node name="HSeparator7" type="HSeparator" parent="VBoxContainer/grid_settings"] -margin_top = 275.0 +margin_top = 228.0 margin_right = 163.0 -margin_bottom = 279.0 +margin_bottom = 232.0 [node name="VSeparator3" type="HSeparator" parent="VBoxContainer/grid_settings"] margin_left = 167.0 -margin_top = 275.0 +margin_top = 228.0 margin_right = 171.0 -margin_bottom = 279.0 +margin_bottom = 232.0 [node name="HSeparator2" type="HSeparator" parent="VBoxContainer/grid_settings"] margin_left = 175.0 -margin_top = 275.0 +margin_top = 228.0 margin_right = 347.0 -margin_bottom = 279.0 +margin_bottom = 232.0 [node name="vbox_player_one_settings" type="VBoxContainer" parent="VBoxContainer/grid_settings"] -margin_top = 283.0 +margin_top = 236.0 margin_right = 163.0 -margin_bottom = 353.0 +margin_bottom = 278.0 [node name="lbl_title_player_one_settings" type="Label" parent="VBoxContainer/grid_settings/vbox_player_one_settings"] margin_right = 163.0 @@ -347,43 +348,44 @@ text = "Player 1:" [node name="grid_player_one_settings" type="GridContainer" parent="VBoxContainer/grid_settings/vbox_player_one_settings"] margin_top = 18.0 margin_right = 163.0 -margin_bottom = 70.0 +margin_bottom = 42.0 columns = 2 [node name="lbl_player_one_length" type="Label" parent="VBoxContainer/grid_settings/vbox_player_one_settings/grid_player_one_settings"] -margin_top = 5.0 -margin_right = 43.0 -margin_bottom = 19.0 +visible = false +margin_right = 58.0 +margin_bottom = 14.0 text = "Length" [node name="input_player_one_length" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_player_one_settings/grid_player_one_settings"] +visible = false margin_left = 47.0 margin_right = 105.0 margin_bottom = 24.0 +editable = false [node name="lbl_player_one_speed" type="Label" parent="VBoxContainer/grid_settings/vbox_player_one_settings/grid_player_one_settings"] -margin_top = 33.0 -margin_right = 43.0 -margin_bottom = 47.0 +margin_top = 5.0 +margin_right = 39.0 +margin_bottom = 19.0 text = "Speed" [node name="input_player_one_speed" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_player_one_settings/grid_player_one_settings"] -margin_left = 47.0 -margin_top = 28.0 -margin_right = 105.0 -margin_bottom = 52.0 +margin_left = 43.0 +margin_right = 101.0 +margin_bottom = 24.0 [node name="VSeparator4" type="VSeparator" parent="VBoxContainer/grid_settings"] margin_left = 167.0 -margin_top = 283.0 +margin_top = 236.0 margin_right = 171.0 -margin_bottom = 353.0 +margin_bottom = 278.0 [node name="vbox_player_two_settings" type="VBoxContainer" parent="VBoxContainer/grid_settings"] margin_left = 175.0 -margin_top = 283.0 +margin_top = 236.0 margin_right = 347.0 -margin_bottom = 353.0 +margin_bottom = 278.0 [node name="lbl_title_player_two_settings" type="Label" parent="VBoxContainer/grid_settings/vbox_player_two_settings"] margin_right = 172.0 @@ -393,53 +395,53 @@ text = "Player 2:" [node name="grid_player_two_settings" type="GridContainer" parent="VBoxContainer/grid_settings/vbox_player_two_settings"] margin_top = 18.0 margin_right = 172.0 -margin_bottom = 70.0 +margin_bottom = 42.0 columns = 2 [node name="lbl_player_two_length" type="Label" parent="VBoxContainer/grid_settings/vbox_player_two_settings/grid_player_two_settings"] -margin_top = 5.0 -margin_right = 43.0 -margin_bottom = 19.0 +visible = false +margin_right = 58.0 +margin_bottom = 14.0 text = "Length" [node name="input_player_two_length" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_player_two_settings/grid_player_two_settings"] +visible = false margin_left = 47.0 margin_right = 105.0 margin_bottom = 24.0 [node name="lbl_player_two_speed" type="Label" parent="VBoxContainer/grid_settings/vbox_player_two_settings/grid_player_two_settings"] -margin_top = 33.0 -margin_right = 43.0 -margin_bottom = 47.0 +margin_top = 5.0 +margin_right = 39.0 +margin_bottom = 19.0 text = "Speed" [node name="input_player_two_speed" type="LineEdit" parent="VBoxContainer/grid_settings/vbox_player_two_settings/grid_player_two_settings"] -margin_left = 47.0 -margin_top = 28.0 -margin_right = 105.0 -margin_bottom = 52.0 +margin_left = 43.0 +margin_right = 101.0 +margin_bottom = 24.0 [node name="VSeparator5" type="HSeparator" parent="VBoxContainer"] -margin_top = 357.0 +margin_top = 282.0 margin_right = 347.0 -margin_bottom = 361.0 +margin_bottom = 286.0 [node name="but_save" type="Button" parent="VBoxContainer"] -margin_top = 365.0 +margin_top = 290.0 margin_right = 347.0 -margin_bottom = 385.0 +margin_bottom = 310.0 text = "Save" [node name="but_cancel" type="Button" parent="VBoxContainer"] -margin_top = 389.0 +margin_top = 314.0 margin_right = 347.0 -margin_bottom = 409.0 +margin_bottom = 334.0 text = "Cancel" [node name="but_reset" type="Button" parent="VBoxContainer"] -margin_top = 413.0 +margin_top = 338.0 margin_right = 347.0 -margin_bottom = 433.0 +margin_bottom = 358.0 text = "Reset" [connection signal="pressed" from="VBoxContainer/grid_settings/vbox_trainer_settings/grid_trainer_settings/input_trainer_position" to="." method="_on_input_trainer_position_pressed"] [connection signal="pressed" from="VBoxContainer/but_save" to="." method="_on_but_save_pressed"] diff --git a/BattlePongAI/ActionSpace.py b/BattlePongAI/ActionSpace.py new file mode 100644 index 0000000000000000000000000000000000000000..056003c49ed15dbb13da90f0d2386e437300fcae --- /dev/null +++ b/BattlePongAI/ActionSpace.py @@ -0,0 +1,8 @@ +from enum import Enum + + +class ActionSpace(Enum): + nothing=0 + up=1 + down=2 + training=3 \ No newline at end of file diff --git a/BattlePongAI/Gym.py b/BattlePongAI/Gym.py new file mode 100644 index 0000000000000000000000000000000000000000..ec68b404774a7d68f90d33fabc50102c99c4da5e --- /dev/null +++ b/BattlePongAI/Gym.py @@ -0,0 +1,171 @@ +import asyncio +import random +from socket import socket +import json +import websockets + +from ActionSpace import ActionSpace +import logging + + +class Gym: + host = None + port = None + player = None + families = None + types = None + protocols = None + logger = None + # Create a TCP/IP socket + sock = None + last_recieved_JSON = None + websocket_server = None + + def __init__(self, host, port, player): + self.host = host + self.port = port + self.player = player + random.seed() + + self.families = self.get_constants('AF_') + self.types = self.get_constants('SOCK_') + self.protocols = self.get_constants('IPPROTO_') + + self.logger = logging.getLogger('websockets') + self.logger.setLevel(logging.INFO) + self.logger.addHandler(logging.StreamHandler()) + print('New gym instance IP:%s Port:%s Playername:%S', host, port, player) + + # Call reset function of environment and returns observation + async def reset(self): + message = '["'+self.player+'","start_game"]' + data = [] + print('send_reset:', message) + await self.send_websocket(message, data) + print("awaited send_reset") + #print(data) + return data + + # Connects the player and returns observation + async def connect_player(self): + message = '["'+self.player+'","connect_player"]' + data = [] + print('connect_player: ', message) + await self.send_websocket(message, data) + print("awaited send_connect_player") + #print(data) + return data + + # TPC - creates env + def open(self, server_ip=None, port=None): + if server_ip is not None: + self.host = server_ip + if port is not None: + self.port = port + # connect to server + self.create_socket(self.host, self.port) + + # TCP - closes the env + def close(self): + # send close to server + # close socket + self.close_socket("Closing environment") + pass + + # send action to env returns observation, reward, done, info + # creates JSON object + async def step(self, action): + if isinstance(action, ActionSpace): + # action is part of ActionSpace + print("go step") + data = await self.send_action(action) + #print('step done', data) + return data + else: + print("Action is not a valid Step") + + """ Returns a random action of action space + """ + def get_sample_action(self): + item = random.randrange(0, len(ActionSpace)) + return ActionSpace(item) + + # socket_echo_client_easy.py + + def get_constants(self, prefix): + """Create a dictionary mapping socket module + constants to their names. + """ + return { + getattr(socket, n): n + for n in dir(socket) + if n.startswith(prefix) + } + + def create_socket(self, server_ip=None, port=None): + if server_ip is not None: + self.host = server_ip + if port is not None: + self.port = port + + # Create a TCP/IP socket + self.sock = socket.create_connection(self.host, self.port) + + print('Family :', self.families[self.sock.family]) + print('Type :', self.types[self.sock.type]) + print('Protocol:', self.protocols[self.sock.proto]) + + async def send_action(self, next_action): + message = '["'+self.player+'","' +self.player+"_"+ next_action.name + '"]' + data = [] + print('send_action:',message) + await self.send_websocket(message, data) + #print("awaited send_action") + #print(data) + return data + + async def send_websocket(self, message, data): + uri = "ws://localhost:9080" + async with websockets.connect(uri) as websocket: + await websocket.send(message) + # print(f"> {name}") + recieved_values = await websocket.recv() + data.append(json.loads(recieved_values)) + #print(f"< {data}") + #print(data) + + async def connect_to_trainer(self): + self.websocket_server = websockets.serve(self.get_training, "localhost", 8765) + + + async def get_training(self): + recieved_values = await self.websocket_server.recv() + print(f"<{recieved_values}") + return json.loads(recieved_values) + + + def send_tcp(self, message): + try: + # Send data + # test message + # message = b'This is the message. It will be repeated.' + print('sending {!r}'.format(message)) + self.sock.sendall(message) + + amount_received = 0 + amount_expected = len(message) + data = None + while amount_received < amount_expected: + data = self.sock.recv(16) + amount_received += len(data) + #print('received {!r}'.format(data)) + return data + + except Exception as e: + self.close_socket("closing socket because of exception {}".format(e.args[-1])) + + # TCP - Closing the socket + def close_socket(self, message): + print("Closing Socket") + print("Msg: " + message) + self.sock.close() diff --git a/BattlePongAI/Observation.py b/BattlePongAI/Observation.py new file mode 100644 index 0000000000000000000000000000000000000000..de7a3f7d13cf83137792864b6ced72e92d285d4f --- /dev/null +++ b/BattlePongAI/Observation.py @@ -0,0 +1,19 @@ +class Observation(): + # picture of the game + picture = None + + # X-Y Coordinates + player_pos = None + + # X-Y Coordinates + enemy_pos = None + + # 2D-Vector + player_direction = None + + # 2D-Vector + enemy_direction = None + + # Given observation will be interpreted + def __init__(self, observation_tcp): + pass \ No newline at end of file diff --git a/BattlePongAI/main.py b/BattlePongAI/main.py new file mode 100644 index 0000000000000000000000000000000000000000..9b5f2856c63f74457273727287fb81fe1edc1c99 --- /dev/null +++ b/BattlePongAI/main.py @@ -0,0 +1,61 @@ +import asyncio + +import show_image as si +from ActionSpace import ActionSpace +from Gym import Gym + + +async def main(): + print("Hello World!") + gym = Gym("localhost", 10000, "player_one") + # data = await gym.step(ActionSpace.UP) + # print('main data', data) + recieved_values = await gym.connect_player() + data = recieved_values[0] + recieved_values = await gym.reset() + data = recieved_values[0] + print('main data', data) + #image_mode = 'RGB' + image_mode = 'L' + try: + # Test show Image + si.show_image(image_mode, data['info']['screenshot']['width'], data['info']['screenshot']['height'], + data['info']['screenshot']['image']) + except Exception as e: + print("failed showing image: " + str(e)) + resetted = True + + while (True): + if data['done']: + recieved_values = await gym.reset() + data = recieved_values[0] + if resetted: + resetted = False + player_one_y = data['observation']['PlayerOne']['Y'] + ball_y = data['observation']['ball']['position']['Y'] + if (player_one_y < ball_y): + recieved_values = await gym.step(ActionSpace.down) + data = recieved_values[0] + print('main data', data) + if (player_one_y > ball_y): + recieved_values = await gym.step(ActionSpace.up) + data = recieved_values[0] + print('main data', data) + if (player_one_y == ball_y): + recieved_values = await gym.step(ActionSpace.nothing) + data = recieved_values[0] + print('main data', data) + try: + # Test show Image + si.show_image(image_mode, data['info']['screenshot']['width'], + data['info']['screenshot']['height'], + data['info']['screenshot']['image']) + except Exception as e: + print("failed showing image: " + str(e)) + + print('done') + + +if __name__ == "__main__": + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) diff --git a/BattlePongAI/main_player_two.py b/BattlePongAI/main_player_two.py new file mode 100644 index 0000000000000000000000000000000000000000..4e6fc2253356a89b37fa1513e15e0d888aeecf3e --- /dev/null +++ b/BattlePongAI/main_player_two.py @@ -0,0 +1,34 @@ +import asyncio + +from ActionSpace import ActionSpace +from Gym import Gym + +async def main(): + print("Hello World!") + gym = Gym("localhost", 9080, "player_two") + + recieved_values= await gym.connect_player() + data = recieved_values[0] + print('main data', data) + while(True): + player_two_y=data['observation']['PlayerTwo']['Y'] + ball_y = data['observation']['ball']['position']['Y'] + if (player_two_y <ball_y): + recieved_values = await gym.step(ActionSpace.down) + data = recieved_values[0] + print('main data', data) + if (player_two_y >ball_y): + recieved_values = await gym.step(ActionSpace.up) + data = recieved_values[0] + print('main data', data) + if (player_two_y ==ball_y): + recieved_values = await gym.step(ActionSpace.nothing) + data = recieved_values[0] + print('main data', data) + + print('done') + +if __name__ == "__main__": + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) + diff --git a/BattlePongAI/main_player_two_training.py b/BattlePongAI/main_player_two_training.py new file mode 100644 index 0000000000000000000000000000000000000000..700d3e5a29e067ef54e64a83a6c67b759fc9dca0 --- /dev/null +++ b/BattlePongAI/main_player_two_training.py @@ -0,0 +1,35 @@ +import asyncio + +import show_image as si +from ActionSpace import ActionSpace +from Gym import Gym + + +async def main(): + print("Hello World!") + gym = Gym("localhost", 9080, "player_one") + + recieved_values = await gym.connect_player() + data = recieved_values[0] + print('main data', data) + while (True): + player_two_y = data['observation']['PlayerTwo']['Y'] + ball_y = data['observation']['ball']['position']['Y'] + + recieved_values = await gym.step(ActionSpace.training) + data = recieved_values[0] + try: + # Test show Image + si.show_image("L", data['info']['screenshot']['width'], + data['info']['screenshot']['height'], + data['info']['screenshot']['image']) + except Exception as e: + print("failed showing image: " + str(e)) + print('main data', data) + + print('done') + + +if __name__ == "__main__": + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) diff --git a/BattlePongAI/show_image.py b/BattlePongAI/show_image.py new file mode 100644 index 0000000000000000000000000000000000000000..8a0e78541b3a3b44a07208edee68675718112926 --- /dev/null +++ b/BattlePongAI/show_image.py @@ -0,0 +1,18 @@ +import numpy as np +import base64 +from PIL import Image +import zlib as zl + + +def show_image(format, width, height, image): + image_decoded = base64.b64decode(image) + print(len(image_decoded)) + print(type(image_decoded)) + #image_data = ... # byte values of the image + image = Image.frombytes(format, (width,height),image_decoded,'raw') + #image.show() + + # Save to file + #a=np.asarray(image) + #im = Image.fromarray(a) + #image.save("your_file.jpeg")