Merge branch 'feature/add_diverse_lv_food' into develop
This commit is contained in:
commit
92c16637af
29
README.md
29
README.md
|
@ -22,20 +22,11 @@
|
||||||
```python
|
```python
|
||||||
# main.py
|
# main.py
|
||||||
game = EasyGame(
|
game = EasyGame(
|
||||||
time_to_play, score_to_pass,
|
level: int = 1,
|
||||||
green_food_count, red_food_count,
|
|
||||||
playground_size: list,
|
|
||||||
level: int = -1,
|
|
||||||
level_file: str = None,
|
level_file: str = None,
|
||||||
sound: str = "off")
|
sound: str = "off")
|
||||||
```
|
```
|
||||||
|
- `level`: 選定內建關卡,預設為 1 選擇第一關。
|
||||||
- `time_to_play`:遊戲執行的終止時間,單位是 frame,也就是遊戲內部更新畫面的次數,每更新一次 frame +1
|
|
||||||
- `green_food_count`:遊戲中綠色食物的數量。
|
|
||||||
- `red_food_count`:遊戲中紅色食物的數量。
|
|
||||||
- `score_to_pass`:遊戲通關的點數,要超過這個分數才算過關。
|
|
||||||
- `playground_size`:可移動區域的大小。 使用逗號將數字隔開 `width,height` `100,200`
|
|
||||||
- `level`: 選定內建關卡,請注意,使用此設定將會覆蓋掉上述其他關卡參數設定,預設為 -1 不選擇任何關卡。
|
|
||||||
- `level_file`: 使用外部檔案作為關卡,請注意,使用此設定將會覆蓋掉關卡編號,並且不會自動進入下一關。
|
- `level_file`: 使用外部檔案作為關卡,請注意,使用此設定將會覆蓋掉關卡編號,並且不會自動進入下一關。
|
||||||
- `sound`: 音效。
|
- `sound`: 音效。
|
||||||
|
|
||||||
|
@ -83,10 +74,10 @@ python -m mlgame -i ./ml/ml_play_template.py ./ --time_to_play 1200 --green_food
|
||||||
import random
|
import random
|
||||||
|
|
||||||
class MLPlay:
|
class MLPlay:
|
||||||
def __init__(self):
|
def __init__(self,ai_name,*args, **kwargs):
|
||||||
print("Initial ml script")
|
print("Initial ml script")
|
||||||
|
|
||||||
def update(self, scene_info: dict):
|
def update(self, scene_info: dict,,*args, **kwargs):
|
||||||
|
|
||||||
# print("AI received data from game :", scene_info)
|
# print("AI received data from game :", scene_info)
|
||||||
|
|
||||||
|
@ -114,12 +105,16 @@ class MLPlay:
|
||||||
"foods": [
|
"foods": [
|
||||||
{
|
{
|
||||||
"x": 656,
|
"x": 656,
|
||||||
"y": 210
|
"y": 210,
|
||||||
|
"type": "GOOD_1",
|
||||||
|
"score": 1
|
||||||
},
|
},
|
||||||
...,
|
...,
|
||||||
{
|
{
|
||||||
"x": 371,
|
"x": 371,
|
||||||
"y": 217
|
"y": 217,
|
||||||
|
"type": "BAD_1",
|
||||||
|
"score": -1
|
||||||
}
|
}
|
||||||
|
|
||||||
],
|
],
|
||||||
|
@ -131,8 +126,10 @@ class MLPlay:
|
||||||
- `frame`:遊戲畫面更新的編號
|
- `frame`:遊戲畫面更新的編號
|
||||||
- `ball_x`:主角方塊的X座標,表示方塊的左邊座標值。
|
- `ball_x`:主角方塊的X座標,表示方塊的左邊座標值。
|
||||||
- `ball_y`:主角方塊的Y座標,表示方塊的上方座標值。
|
- `ball_y`:主角方塊的Y座標,表示方塊的上方座標值。
|
||||||
- `foods`:食物的清單,清單內每一個物件都是一個食物的左上方座標值
|
- `foods`:食物的清單,清單內每一個物件都是一個食物的左上方座標值,也會提供此食物是什麼類型和分數多少。
|
||||||
|
- `type` 食物類型: `GOOD_1`, `GOOD_2`, `GOOD_3`, `BAD_1`, `BAD_2`, `BAD_3`
|
||||||
- `score`:目前得到的分數
|
- `score`:目前得到的分數
|
||||||
|
- `score_to_pass`:通關分數
|
||||||
- `status`: 目前遊戲的狀態
|
- `status`: 目前遊戲的狀態
|
||||||
- `GAME_ALIVE`:遊戲進行中
|
- `GAME_ALIVE`:遊戲進行中
|
||||||
- `GAME_PASS`:遊戲通關
|
- `GAME_PASS`:遊戲通關
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
["scene_info['ball_y']", "y coordinate of ball", "球的 y 座標"],
|
["scene_info['ball_y']", "y coordinate of ball", "球的 y 座標"],
|
||||||
["scene_info['score']", "score", "目前的分數"],
|
["scene_info['score']", "score", "目前的分數"],
|
||||||
["scene_info['foods']", "list of foods positions", "點點的位置清單"],
|
["scene_info['foods']", "list of foods positions", "點點的位置清單"],
|
||||||
|
["scene_info['score_to_pass']", "the score for next level", "通關分數"],
|
||||||
["scene_info", "dictionary of all information", "包含所有資訊的字典"]
|
["scene_info", "dictionary of all information", "包含所有資訊的字典"]
|
||||||
],
|
],
|
||||||
"CONSTANT": [
|
"CONSTANT": [
|
||||||
|
@ -22,8 +23,9 @@
|
||||||
[600, "bottom boundary", "下邊界"],
|
[600, "bottom boundary", "下邊界"],
|
||||||
[30, "ball width", "球身的寬度"],
|
[30, "ball width", "球身的寬度"],
|
||||||
[30, "ball height", "球身的高度"],
|
[30, "ball height", "球身的高度"],
|
||||||
[8, "food width", "食物的寬度"],
|
[8, "food lv1 size", "等級一食物的寬高度"],
|
||||||
[8, "food height", "食物的高度"]
|
[12, "food lv2 size", "等級二食物的高度"],
|
||||||
|
[16, "food lv3 size", "等級三食物的高度"]
|
||||||
],
|
],
|
||||||
"ACTION": [
|
"ACTION": [
|
||||||
["['UP']", "moving up", "向上移動"],
|
["['UP']", "moving up", "向上移動"],
|
||||||
|
|
|
@ -12,77 +12,14 @@
|
||||||
"max": 1
|
"max": 1
|
||||||
},
|
},
|
||||||
"game_params": [
|
"game_params": [
|
||||||
{
|
|
||||||
"name": "time_to_play",
|
|
||||||
"verbose": "遊戲總幀數",
|
|
||||||
"type": "int",
|
|
||||||
"max": 2000,
|
|
||||||
"min": 600,
|
|
||||||
"default": 600,
|
|
||||||
"help": "set the limit of frame count , actually time will be revised according to your FPS ."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "green_food_count",
|
|
||||||
"verbose": "綠色食物數量",
|
|
||||||
"type": "int",
|
|
||||||
"choices": [
|
|
||||||
5,
|
|
||||||
10,
|
|
||||||
15,
|
|
||||||
20,
|
|
||||||
25,
|
|
||||||
30,
|
|
||||||
35,
|
|
||||||
40,
|
|
||||||
45,
|
|
||||||
50
|
|
||||||
],
|
|
||||||
"help": "set the total number of points",
|
|
||||||
"default": 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "red_food_count",
|
|
||||||
"verbose": "紅色食物數量",
|
|
||||||
"type": "int",
|
|
||||||
"choices": [
|
|
||||||
5,
|
|
||||||
10,
|
|
||||||
15,
|
|
||||||
20,
|
|
||||||
25,
|
|
||||||
30,
|
|
||||||
35,
|
|
||||||
40,
|
|
||||||
45,
|
|
||||||
50
|
|
||||||
],
|
|
||||||
"help": "set the total number of points",
|
|
||||||
"default": 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "score_to_pass",
|
|
||||||
"verbose": "通關分數",
|
|
||||||
"type": "int",
|
|
||||||
"min": 1,
|
|
||||||
"max": 30,
|
|
||||||
"default": 10,
|
|
||||||
"help": "set the score to win this game "
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "playground_size",
|
|
||||||
"verbose": "場地尺寸",
|
|
||||||
"type": "list",
|
|
||||||
"default": "400,400",
|
|
||||||
"help": "set the size of playground "
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "level",
|
"name": "level",
|
||||||
"verbose": "內建關卡編號",
|
"verbose": "內建關卡編號",
|
||||||
"type": "int",
|
"type": "int",
|
||||||
"min": -1,
|
"min": -1,
|
||||||
"max": 5,
|
"max": 100,
|
||||||
"default": "-1",
|
"default": 1,
|
||||||
"help": "選定內建關卡,請注意,使用此設定將會覆蓋掉其他關卡設定,預設為 -1 不選擇任何關卡。"
|
"help": "選定內建關卡,預設為 1 選擇第一關。"
|
||||||
}, {
|
}, {
|
||||||
"name": "level_file",
|
"name": "level_file",
|
||||||
"verbose": "匯入關卡檔案",
|
"verbose": "匯入關卡檔案",
|
||||||
|
|
|
@ -5,6 +5,6 @@
|
||||||
200
|
200
|
||||||
],
|
],
|
||||||
"score_to_pass": 10,
|
"score_to_pass": 10,
|
||||||
"green_food_count": 3,
|
"green_food_count": [3,0,0],
|
||||||
"black_food_count": 0
|
"black_food_count": [0,0,0]
|
||||||
}
|
}
|
|
@ -5,6 +5,6 @@
|
||||||
200
|
200
|
||||||
],
|
],
|
||||||
"score_to_pass": 15,
|
"score_to_pass": 15,
|
||||||
"green_food_count": 5,
|
"green_food_count": [5,0,0],
|
||||||
"black_food_count": 0
|
"black_food_count": [0,0,0]
|
||||||
}
|
}
|
|
@ -5,6 +5,6 @@
|
||||||
300
|
300
|
||||||
],
|
],
|
||||||
"score_to_pass": 15,
|
"score_to_pass": 15,
|
||||||
"green_food_count": 10,
|
"green_food_count": [6,0,0],
|
||||||
"black_food_count": 0
|
"black_food_count": [2,0,0]
|
||||||
}
|
}
|
|
@ -5,6 +5,6 @@
|
||||||
300
|
300
|
||||||
],
|
],
|
||||||
"score_to_pass": 15,
|
"score_to_pass": 15,
|
||||||
"green_food_count": 7,
|
"green_food_count": [8,0,0],
|
||||||
"black_food_count": 3
|
"black_food_count": [4,0,0]
|
||||||
}
|
}
|
|
@ -5,6 +5,6 @@
|
||||||
300
|
300
|
||||||
],
|
],
|
||||||
"score_to_pass": 15,
|
"score_to_pass": 15,
|
||||||
"green_food_count": 7,
|
"green_food_count": [8,3,0],
|
||||||
"black_food_count": 7
|
"black_food_count": [4,0,0]
|
||||||
}
|
}
|
|
@ -5,6 +5,6 @@
|
||||||
400
|
400
|
||||||
],
|
],
|
||||||
"score_to_pass": 15,
|
"score_to_pass": 15,
|
||||||
"green_food_count": 7,
|
"green_food_count": [8,4,0],
|
||||||
"black_food_count": 10
|
"black_food_count": [5,2,0]
|
||||||
}
|
}
|
|
@ -5,6 +5,6 @@
|
||||||
400
|
400
|
||||||
],
|
],
|
||||||
"score_to_pass": 20,
|
"score_to_pass": 20,
|
||||||
"green_food_count": 7,
|
"green_food_count": [8,4,2],
|
||||||
"black_food_count": 13
|
"black_food_count": [4,4,0]
|
||||||
}
|
}
|
|
@ -5,6 +5,6 @@
|
||||||
500
|
500
|
||||||
],
|
],
|
||||||
"score_to_pass": 20,
|
"score_to_pass": 20,
|
||||||
"green_food_count": 10,
|
"green_food_count": [6,4,2],
|
||||||
"black_food_count": 15
|
"black_food_count": [6,4,2]
|
||||||
}
|
}
|
|
@ -5,6 +5,6 @@
|
||||||
500
|
500
|
||||||
],
|
],
|
||||||
"score_to_pass": 25,
|
"score_to_pass": 25,
|
||||||
"green_food_count": 15,
|
"green_food_count": [10,5,3],
|
||||||
"black_food_count": 30
|
"black_food_count": [12,7,4]
|
||||||
}
|
}
|
|
@ -5,6 +5,6 @@
|
||||||
600
|
600
|
||||||
],
|
],
|
||||||
"score_to_pass": 25,
|
"score_to_pass": 25,
|
||||||
"green_food_count": 20,
|
"green_food_count": [12,8,4],
|
||||||
"black_food_count": 40
|
"black_food_count": [25,10,5]
|
||||||
}
|
}
|
|
@ -1,7 +1,11 @@
|
||||||
{
|
{
|
||||||
"time_to_play": 1200,
|
"time_to_play": 300,
|
||||||
"playground_size": [
|
"playground_size": [
|
||||||
500,
|
100,
|
||||||
200
|
200
|
||||||
]
|
],
|
||||||
|
"score_to_pass": 10,
|
||||||
|
"green_food_count": [3,0,0],
|
||||||
|
"black_food_count": [0,0,0]
|
||||||
|
|
||||||
}
|
}
|
22
src/env.py
22
src/env.py
|
@ -17,10 +17,24 @@ BALL_W = 30
|
||||||
|
|
||||||
# food
|
# food
|
||||||
class FoodTypeEnum(StringEnum):
|
class FoodTypeEnum(StringEnum):
|
||||||
GREEN = auto()
|
GOOD_1 = auto()
|
||||||
RED = auto()
|
GOOD_2 = auto()
|
||||||
FOOD_COLOR_MAP = {FoodTypeEnum.GREEN: "#009688",
|
GOOD_3 = auto()
|
||||||
FoodTypeEnum.RED: "#FF1744"}
|
BAD_1 = auto()
|
||||||
|
BAD_2 = auto()
|
||||||
|
BAD_3 = auto()
|
||||||
|
|
||||||
|
FOOD_COLOR_MAP = {
|
||||||
|
FoodTypeEnum.GOOD_1: "#009688",
|
||||||
|
FoodTypeEnum.GOOD_2: "#009688",
|
||||||
|
FoodTypeEnum.GOOD_3: "#009688",
|
||||||
|
FoodTypeEnum.BAD_1: "#FF1744",
|
||||||
|
FoodTypeEnum.BAD_2: "#FF1744",
|
||||||
|
FoodTypeEnum.BAD_3: "#FF1744"
|
||||||
|
}
|
||||||
|
FOOD_LV1_SIZE = 8
|
||||||
|
FOOD_LV2_SIZE = 12
|
||||||
|
FOOD_LV3_SIZE = 16
|
||||||
|
|
||||||
# path of assets
|
# path of assets
|
||||||
ASSET_PATH = path.join(path.dirname(__file__), "..", "asset")
|
ASSET_PATH = path.join(path.dirname(__file__), "..", "asset")
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
import pygame.sprite
|
||||||
|
|
||||||
|
from games.easy_game.src.env import FoodTypeEnum, FOOD_COLOR_MAP, FOOD_LV1_SIZE, FOOD_LV2_SIZE, FOOD_LV3_SIZE
|
||||||
|
from mlgame.view.view_model import create_rect_view_data
|
||||||
|
|
||||||
|
|
||||||
|
class Food(pygame.sprite.Sprite):
|
||||||
|
def __init__(self, group):
|
||||||
|
pygame.sprite.Sprite.__init__(self, group)
|
||||||
|
self.image = pygame.Surface([8, 8])
|
||||||
|
self.type = None
|
||||||
|
self.score = 0
|
||||||
|
self.color = None
|
||||||
|
|
||||||
|
self.rect = self.image.get_rect()
|
||||||
|
self.angle = 0
|
||||||
|
|
||||||
|
def update(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def game_object_data(self):
|
||||||
|
return create_rect_view_data(
|
||||||
|
"food",
|
||||||
|
self.rect.x,
|
||||||
|
self.rect.y,
|
||||||
|
self.rect.width,
|
||||||
|
self.rect.height,
|
||||||
|
self.color
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GoodFoodLv1(Food):
|
||||||
|
def __init__(self, group):
|
||||||
|
super().__init__(group)
|
||||||
|
self.image = pygame.Surface([FOOD_LV1_SIZE, FOOD_LV1_SIZE])
|
||||||
|
self.type = FoodTypeEnum.GOOD_1
|
||||||
|
self.color = FOOD_COLOR_MAP[self.type]
|
||||||
|
self.score = 1
|
||||||
|
self.rect = self.image.get_rect()
|
||||||
|
class GoodFoodLv2(Food):
|
||||||
|
def __init__(self, group):
|
||||||
|
super().__init__(group)
|
||||||
|
|
||||||
|
self.image = pygame.Surface([FOOD_LV2_SIZE, FOOD_LV2_SIZE])
|
||||||
|
self.type = FoodTypeEnum.GOOD_2
|
||||||
|
self.color = FOOD_COLOR_MAP[self.type]
|
||||||
|
self.score = 2
|
||||||
|
self.rect = self.image.get_rect()
|
||||||
|
|
||||||
|
class GoodFoodLv3(Food):
|
||||||
|
def __init__(self, group):
|
||||||
|
super().__init__(group)
|
||||||
|
|
||||||
|
self.image = pygame.Surface([FOOD_LV3_SIZE, FOOD_LV3_SIZE])
|
||||||
|
self.type = FoodTypeEnum.GOOD_3
|
||||||
|
self.color = FOOD_COLOR_MAP[self.type]
|
||||||
|
self.score = 4
|
||||||
|
self.rect = self.image.get_rect()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BadFoodLv1(Food):
|
||||||
|
def __init__(self, group):
|
||||||
|
super().__init__(group)
|
||||||
|
self.image = pygame.Surface([FOOD_LV1_SIZE, FOOD_LV1_SIZE])
|
||||||
|
self.type = FoodTypeEnum.BAD_1
|
||||||
|
self.color = FOOD_COLOR_MAP[self.type]
|
||||||
|
self.score = -1
|
||||||
|
self.rect = self.image.get_rect()
|
||||||
|
self.angle = 0
|
||||||
|
|
||||||
|
class BadFoodLv2(Food):
|
||||||
|
def __init__(self, group):
|
||||||
|
super().__init__(group)
|
||||||
|
|
||||||
|
self.image = pygame.Surface([FOOD_LV2_SIZE, FOOD_LV2_SIZE])
|
||||||
|
self.type = FoodTypeEnum.BAD_2
|
||||||
|
self.color = FOOD_COLOR_MAP[self.type]
|
||||||
|
self.score = -2
|
||||||
|
self.rect = self.image.get_rect()
|
||||||
|
|
||||||
|
class BadFoodLv3(Food):
|
||||||
|
def __init__(self, group):
|
||||||
|
super().__init__(group)
|
||||||
|
|
||||||
|
self.image = pygame.Surface([FOOD_LV3_SIZE, FOOD_LV3_SIZE])
|
||||||
|
self.type = FoodTypeEnum.BAD_3
|
||||||
|
self.color = FOOD_COLOR_MAP[self.type]
|
||||||
|
self.score = -4
|
||||||
|
self.rect = self.image.get_rect()
|
176
src/game.py
176
src/game.py
|
@ -9,65 +9,62 @@ from mlgame.utils.enum import get_ai_name
|
||||||
from mlgame.view.decorator import check_game_progress, check_game_result
|
from mlgame.view.decorator import check_game_progress, check_game_result
|
||||||
from mlgame.view.view_model import *
|
from mlgame.view.view_model import *
|
||||||
from .env import *
|
from .env import *
|
||||||
from .env import FoodTypeEnum
|
from .foods import *
|
||||||
from .game_object import Ball, Food
|
from .game_object import Ball
|
||||||
from .sound_controller import SoundController
|
from .sound_controller import SoundController
|
||||||
|
|
||||||
|
|
||||||
|
def revise_ball(ball: Ball, playground: pygame.Rect):
|
||||||
|
ball_rect = copy.deepcopy(ball.rect)
|
||||||
|
if ball_rect.left < playground.left:
|
||||||
|
ball_rect.left = playground.left
|
||||||
|
elif ball_rect.right > playground.right:
|
||||||
|
ball_rect.right = playground.right
|
||||||
|
|
||||||
|
if ball_rect.top < playground.top:
|
||||||
|
ball_rect.top = playground.top
|
||||||
|
elif ball_rect.bottom > playground.bottom:
|
||||||
|
ball_rect.bottom = playground.bottom
|
||||||
|
ball.rect = ball_rect
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class EasyGame(PaiaGame):
|
class EasyGame(PaiaGame):
|
||||||
"""
|
"""
|
||||||
This is a Interface of a game
|
This is a Interface of a game
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, time_to_play, score_to_pass, green_food_count, red_food_count,
|
self,
|
||||||
playground_size: list,
|
level: int = 1,
|
||||||
level: int = -1,
|
|
||||||
level_file: str = None,
|
level_file: str = None,
|
||||||
sound: str = "off",
|
sound: str = "off",
|
||||||
*args, **kwargs):
|
*args, **kwargs):
|
||||||
super().__init__(user_num=1)
|
super().__init__(user_num=1)
|
||||||
|
# TODO reduce game config and use level file
|
||||||
self.game_result_state = GameResultState.FAIL
|
self.game_result_state = GameResultState.FAIL
|
||||||
self.scene = Scene(width=WIDTH, height=HEIGHT, color=BG_COLOR, bias_x=0, bias_y=0)
|
self.scene = Scene(width=WIDTH, height=HEIGHT, color=BG_COLOR, bias_x=0, bias_y=0)
|
||||||
self._level = level
|
self._level = level
|
||||||
self._level_file = level_file
|
self._level_file = level_file
|
||||||
if self._level_file is not None or level == "":
|
|
||||||
# set by injected file
|
|
||||||
self.set_game_params_by_file(self._level_file)
|
|
||||||
pass
|
|
||||||
elif self._level != -1:
|
|
||||||
# set by level number
|
|
||||||
self.set_game_params_by_level(self._level)
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
# set by game params
|
|
||||||
self._playground_w = int(playground_size[0])
|
|
||||||
self._playground_h = int(playground_size[1])
|
|
||||||
self.playground = pygame.Rect(
|
|
||||||
0, 0,
|
|
||||||
self._playground_w,
|
|
||||||
self._playground_h
|
|
||||||
)
|
|
||||||
self._green_food_count = green_food_count
|
|
||||||
self._red_food_count = red_food_count
|
|
||||||
self._score_to_pass = score_to_pass
|
|
||||||
self._frame_limit = time_to_play
|
|
||||||
self.playground.center = (WIDTH / 2, HEIGHT / 2)
|
|
||||||
|
|
||||||
self.foods = pygame.sprite.Group()
|
self.foods = pygame.sprite.Group()
|
||||||
self.sound_controller = SoundController(sound)
|
self.sound_controller = SoundController(sound)
|
||||||
self.init_game()
|
|
||||||
|
|
||||||
def set_game_params_by_level(self, level):
|
if path.isfile(self._level_file) or self._level_file == "":
|
||||||
|
# set by injected file
|
||||||
|
self._init_game_by_file(self._level_file)
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
self._init_game_by_level(self._level)
|
||||||
|
|
||||||
|
def _init_game_by_level(self, level: int):
|
||||||
level_file_path = os.path.join(LEVEL_PATH, f"{level:03d}.json")
|
level_file_path = os.path.join(LEVEL_PATH, f"{level:03d}.json")
|
||||||
self.set_game_params_by_file(level_file_path)
|
self._init_game_by_file(level_file_path)
|
||||||
|
|
||||||
def set_game_params_by_file(self, level_file_path: str):
|
def _init_game_by_file(self, level_file_path: str):
|
||||||
try:
|
try:
|
||||||
with open(level_file_path) as f:
|
with open(level_file_path) as f:
|
||||||
game_params = json.load(f)
|
game_params = json.load(f)
|
||||||
self._set_game_params(game_params)
|
|
||||||
except:
|
except:
|
||||||
# If the file doesn't exist, use default parameters
|
# If the file doesn't exist, use default parameters
|
||||||
print("此關卡檔案不存在,遊戲將會會自動使用第一關檔案 001.json。")
|
print("此關卡檔案不存在,遊戲將會會自動使用第一關檔案 001.json。")
|
||||||
|
@ -75,32 +72,40 @@ class EasyGame(PaiaGame):
|
||||||
with open(os.path.join(LEVEL_PATH, "001.json")) as f:
|
with open(os.path.join(LEVEL_PATH, "001.json")) as f:
|
||||||
game_params = json.load(f)
|
game_params = json.load(f)
|
||||||
self._level = 1
|
self._level = 1
|
||||||
self._level_file=None
|
self._level_file = None
|
||||||
self._set_game_params(game_params)
|
finally:
|
||||||
|
# set game params
|
||||||
|
self._playground_w = int(game_params["playground_size"][0])
|
||||||
|
self._playground_h = int(game_params["playground_size"][1])
|
||||||
|
self.playground = pygame.Rect(
|
||||||
|
0, 0,
|
||||||
|
self._playground_w,
|
||||||
|
self._playground_h
|
||||||
|
)
|
||||||
|
self._green_food_count = game_params["green_food_count"]
|
||||||
|
self._red_food_count = game_params["black_food_count"]
|
||||||
|
self._score_to_pass = int(game_params["score_to_pass"])
|
||||||
|
self._frame_limit = int(game_params["time_to_play"])
|
||||||
|
self.playground.center = (WIDTH / 2, HEIGHT / 2)
|
||||||
|
|
||||||
|
# init game
|
||||||
|
self.ball = Ball()
|
||||||
|
self.foods.empty()
|
||||||
|
self.score = 0
|
||||||
|
|
||||||
|
# todo validate food count
|
||||||
|
self._create_foods(GoodFoodLv1, self._green_food_count[0])
|
||||||
|
self._create_foods(GoodFoodLv2, self._green_food_count[1])
|
||||||
|
self._create_foods(GoodFoodLv3, self._green_food_count[2])
|
||||||
|
self._create_foods(BadFoodLv1, self._red_food_count[0])
|
||||||
|
self._create_foods(BadFoodLv2, self._red_food_count[1])
|
||||||
|
self._create_foods(BadFoodLv3, self._red_food_count[2])
|
||||||
|
|
||||||
|
self.frame_count = 0
|
||||||
|
self._frame_count_down = self._frame_limit
|
||||||
|
self.sound_controller.play_music()
|
||||||
|
|
||||||
def _set_game_params(self, game_params):
|
|
||||||
self._playground_w = int(game_params["playground_size"][0])
|
|
||||||
self._playground_h = int(game_params["playground_size"][1])
|
|
||||||
self.playground = pygame.Rect(
|
|
||||||
0, 0,
|
|
||||||
self._playground_w,
|
|
||||||
self._playground_h
|
|
||||||
)
|
|
||||||
self._green_food_count = int(game_params["green_food_count"])
|
|
||||||
self._red_food_count = int(game_params["black_food_count"])
|
|
||||||
self._score_to_pass = int(game_params["score_to_pass"])
|
|
||||||
self._frame_limit = int(game_params["time_to_play"])
|
|
||||||
self.playground.center = (WIDTH / 2, HEIGHT / 2)
|
|
||||||
|
|
||||||
def init_game(self):
|
|
||||||
self.ball = Ball()
|
|
||||||
self.foods.empty()
|
|
||||||
self.score = 0
|
|
||||||
self._create_foods(self._green_food_count, FoodTypeEnum.GREEN)
|
|
||||||
self._create_foods(self._red_food_count, FoodTypeEnum.RED)
|
|
||||||
self.frame_count = 0
|
|
||||||
self._frame_count_down = self._frame_limit
|
|
||||||
self.sound_controller.play_music()
|
|
||||||
|
|
||||||
def update(self, commands):
|
def update(self, commands):
|
||||||
# handle command
|
# handle command
|
||||||
|
@ -111,24 +116,13 @@ class EasyGame(PaiaGame):
|
||||||
action = "NONE"
|
action = "NONE"
|
||||||
|
|
||||||
self.ball.update(action)
|
self.ball.update(action)
|
||||||
self.revise_ball(self.ball, self.playground)
|
revise_ball(self.ball, self.playground)
|
||||||
# update sprite
|
# update sprite
|
||||||
self.foods.update()
|
self.foods.update()
|
||||||
|
|
||||||
# handle collision
|
# handle collision
|
||||||
hits = pygame.sprite.spritecollide(self.ball, self.foods, True)
|
|
||||||
if hits:
|
|
||||||
|
|
||||||
for food in hits:
|
self._check_foods_collision()
|
||||||
if food.type == FoodTypeEnum.GREEN:
|
|
||||||
self.sound_controller.play_eating_good()
|
|
||||||
self.score += 1
|
|
||||||
self._create_foods(1, FoodTypeEnum.GREEN)
|
|
||||||
|
|
||||||
elif food.type == FoodTypeEnum.RED:
|
|
||||||
self.sound_controller.play_eating_bad()
|
|
||||||
self._create_foods(1, FoodTypeEnum.RED)
|
|
||||||
self.score -= 1
|
|
||||||
# self._timer = round(time.time() - self._begin_time, 3)
|
# self._timer = round(time.time() - self._begin_time, 3)
|
||||||
|
|
||||||
self.frame_count += 1
|
self.frame_count += 1
|
||||||
|
@ -138,6 +132,17 @@ class EasyGame(PaiaGame):
|
||||||
if not self.is_running:
|
if not self.is_running:
|
||||||
return "RESET"
|
return "RESET"
|
||||||
|
|
||||||
|
def _check_foods_collision(self):
|
||||||
|
hits = pygame.sprite.spritecollide(self.ball, self.foods, True)
|
||||||
|
if hits:
|
||||||
|
for food in hits:
|
||||||
|
self.score += food.score
|
||||||
|
self._create_foods(food.__class__, 1)
|
||||||
|
if isinstance(food, (GoodFoodLv1,GoodFoodLv2,GoodFoodLv3,)):
|
||||||
|
self.sound_controller.play_eating_good()
|
||||||
|
elif isinstance(food, (BadFoodLv1,BadFoodLv2,BadFoodLv3,)):
|
||||||
|
self.sound_controller.play_eating_bad()
|
||||||
|
|
||||||
def get_data_from_game_to_player(self):
|
def get_data_from_game_to_player(self):
|
||||||
"""
|
"""
|
||||||
send something to game AI
|
send something to game AI
|
||||||
|
@ -146,13 +151,16 @@ class EasyGame(PaiaGame):
|
||||||
to_players_data = {}
|
to_players_data = {}
|
||||||
foods_data = []
|
foods_data = []
|
||||||
for food in self.foods:
|
for food in self.foods:
|
||||||
foods_data.append({"x": food.rect.x, "y": food.rect.y})
|
# TODO add good food and bad food
|
||||||
|
|
||||||
|
foods_data.append({"x": food.rect.x, "y": food.rect.y, "type": food.type, "score": food.score})
|
||||||
data_to_1p = {
|
data_to_1p = {
|
||||||
"frame": self.frame_count,
|
"frame": self.frame_count,
|
||||||
"ball_x": self.ball.rect.centerx,
|
"ball_x": self.ball.rect.centerx,
|
||||||
"ball_y": self.ball.rect.centery,
|
"ball_y": self.ball.rect.centery,
|
||||||
"foods": foods_data,
|
"foods": foods_data,
|
||||||
"score": self.score,
|
"score": self.score,
|
||||||
|
"score_to_pass":self._score_to_pass,
|
||||||
"status": self.get_game_status()
|
"status": self.get_game_status()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,9 +189,9 @@ class EasyGame(PaiaGame):
|
||||||
elif self.is_passed and self._level != -1:
|
elif self.is_passed and self._level != -1:
|
||||||
# win and use level will enter next level
|
# win and use level will enter next level
|
||||||
self._level += 1
|
self._level += 1
|
||||||
self.set_game_params_by_level(self._level)
|
self._init_game_by_level(self._level)
|
||||||
|
|
||||||
self.init_game()
|
self._init_game()
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -199,7 +207,6 @@ class EasyGame(PaiaGame):
|
||||||
"""
|
"""
|
||||||
Get the initial scene and object information for drawing on the web
|
Get the initial scene and object information for drawing on the web
|
||||||
"""
|
"""
|
||||||
# TODO add music or sound
|
|
||||||
# bg_path = path.join(ASSET_PATH, "img/background.jpg")
|
# bg_path = path.join(ASSET_PATH, "img/background.jpg")
|
||||||
# background = create_asset_init_data(
|
# background = create_asset_init_data(
|
||||||
# "background", WIDTH, HEIGHT, bg_path,
|
# "background", WIDTH, HEIGHT, bg_path,
|
||||||
|
@ -280,25 +287,10 @@ class EasyGame(PaiaGame):
|
||||||
cmd_1p.append("NONE")
|
cmd_1p.append("NONE")
|
||||||
return {get_ai_name(0): cmd_1p}
|
return {get_ai_name(0): cmd_1p}
|
||||||
|
|
||||||
def _create_foods(self, count: int = 5, type: FoodTypeEnum = FoodTypeEnum.GREEN):
|
def _create_foods(self, FOOD_TYPE, count: int = 5):
|
||||||
for i in range(count):
|
for i in range(count):
|
||||||
# add food to group
|
# add food to group
|
||||||
food = Food(self.foods, type)
|
food = FOOD_TYPE(self.foods)
|
||||||
|
|
||||||
food.rect.centerx = random.randint(self.playground.left, self.playground.right)
|
food.rect.centerx = random.randint(self.playground.left, self.playground.right)
|
||||||
food.rect.centery = random.randint(self.playground.top, self.playground.bottom)
|
food.rect.centery = random.randint(self.playground.top, self.playground.bottom)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def revise_ball(self, ball: Ball, playground: pygame.Rect):
|
|
||||||
ball_rect = copy.deepcopy(ball.rect)
|
|
||||||
if ball_rect.left < playground.left:
|
|
||||||
ball_rect.left = playground.left
|
|
||||||
elif ball_rect.right > playground.right:
|
|
||||||
ball_rect.right = playground.right
|
|
||||||
|
|
||||||
if ball_rect.top < playground.top:
|
|
||||||
ball_rect.top = playground.top
|
|
||||||
elif ball_rect.bottom > playground.bottom:
|
|
||||||
ball_rect.bottom = playground.bottom
|
|
||||||
ball.rect = ball_rect
|
|
||||||
pass
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import pygame.sprite
|
import pygame.sprite
|
||||||
|
|
||||||
from games.easy_game.src.env import FoodTypeEnum, FOOD_COLOR_MAP, BALL_COLOR, BALL_VEL, BALL_H, BALL_W
|
from games.easy_game.src.env import BALL_COLOR, BALL_VEL, BALL_H, BALL_W
|
||||||
|
from games.easy_game.src.foods import Food
|
||||||
from mlgame.view.view_model import create_rect_view_data
|
from mlgame.view.view_model import create_rect_view_data
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,26 +45,3 @@ class Ball(pygame.sprite.Sprite):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Food(pygame.sprite.Sprite):
|
|
||||||
def __init__(self, group, type: FoodTypeEnum):
|
|
||||||
pygame.sprite.Sprite.__init__(self, group)
|
|
||||||
self.image = pygame.Surface([8, 8])
|
|
||||||
self.type = type
|
|
||||||
self.color = FOOD_COLOR_MAP[type]
|
|
||||||
|
|
||||||
self.rect = self.image.get_rect()
|
|
||||||
self.angle = 0
|
|
||||||
|
|
||||||
def update(self) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
|
||||||
def game_object_data(self):
|
|
||||||
return create_rect_view_data(
|
|
||||||
"food",
|
|
||||||
self.rect.x,
|
|
||||||
self.rect.y,
|
|
||||||
self.rect.width,
|
|
||||||
self.rect.height,
|
|
||||||
self.color
|
|
||||||
)
|
|
||||||
|
|
Loading…
Reference in New Issue