Update socket output

This commit is contained in:
Nguyễn Gia Phong 2018-08-05 18:05:07 +07:00
parent 834fa33ec0
commit ba2aaeb1f1
6 changed files with 47 additions and 62 deletions

View File

@ -21,6 +21,7 @@ Brutal Maze has a few notable features:
* Somewhat a realistic physic and logic system.
* Resizable game window in-game.
* Easily customizable via INI file format.
* Recordable in JSON (some kind of silent screencast).
* Remote control through TCP/IP socket (can be used in AI researching).
Installation

View File

@ -17,17 +17,16 @@
# You should have received a copy of the GNU Affero General Public License
# along with Brutal Maze. If not, see <https://www.gnu.org/licenses/>.
__version__ = '0.8.1'
__version__ = '0.8.20'
import re
from argparse import ArgumentParser, FileType, RawTextHelpFormatter
from collections import deque
try: # Python 3
from configparser import ConfigParser
except ImportError: # Python 2
from ConfigParser import ConfigParser
from math import atan2, radians, pi
from os.path import join, pathsep
from os.path import join as pathjoin, pathsep
from socket import socket, SOL_SOCKET, SO_REUSEADDR
from sys import stdout
from threading import Thread
@ -37,10 +36,9 @@ from pygame import KEYDOWN, QUIT, VIDEORESIZE
from pygame.time import Clock, get_ticks
from appdirs import AppDirs
from .constants import (
SETTINGS, ICON, MUSIC, NOISE, HERO_SPEED, COLORS, MIDDLE, WALL)
from .constants import SETTINGS, ICON, MUSIC, NOISE, HERO_SPEED, MIDDLE
from .maze import Maze
from .misc import deg, round2, sign
from .misc import sign, deg, join
class ConfigReader:
@ -73,6 +71,8 @@ class ConfigReader:
self.muted = self.config.getboolean('Sound', 'Muted')
self.musicvol = self.config.getfloat('Sound', 'Music volume')
self.space = self.config.getboolean('Sound', 'Space theme')
self.export_dir = self.config.get('Record', 'Directory')
self.export_rate = self.config.getint('Record', 'Frequency')
self.server = self.config.getboolean('Server', 'Enable')
self.host = self.config.get('Server', 'Host')
self.port = self.config.getint('Server', 'Port')
@ -98,8 +98,9 @@ class ConfigReader:
def read_args(self, arguments):
"""Read and parse a ArgumentParser.Namespace."""
for option in ('size', 'max_fps', 'muted', 'musicvol', 'space',
'server', 'host', 'port', 'timeout', 'headless'):
for option in (
'size', 'max_fps', 'muted', 'musicvol', 'space', 'export_dir',
'export_rate', 'server', 'host', 'port', 'timeout', 'headless'):
value = getattr(arguments, option)
if value is not None: setattr(self, option, value)
@ -134,51 +135,20 @@ class Game:
self.max_fps, self.fps = config.max_fps, float(config.max_fps)
self.musicvol = config.musicvol
self.key, self.mouse = config.key, config.mouse
self.maze = Maze(config.max_fps, config.size, config.headless)
self.maze = Maze(config.max_fps, config.size, config.headless,
config.export_dir, 1000.0 / config.export_rate)
self.hero = self.maze.hero
self.clock, self.paused = Clock(), False
def __enter__(self): return self
def expos(self, x, y):
"""Return position of the given coordinates in rounded percent."""
cx = (x+self.maze.x-self.maze.centerx) / self.maze.distance * 100
cy = (y+self.maze.y-self.maze.centery) / self.maze.distance * 100
return round2(cx), round2(cy)
def export(self):
"""Export maze data to a bytes object."""
maze, hero, = self.maze, self.hero
lines = deque(['{0} {4} {5} {1} {2:d} {3:d}'.format(
COLORS[hero.get_color()], deg(self.hero.angle),
hero.next_strike <= 0, hero.next_heal <= 0,
*self.expos(maze.x, maze.y))])
walls = [[1 if maze.map[x][y] == WALL else 0 for x in maze.rangex]
for y in maze.rangey] if maze.next_move <= 0 else []
ne = nb = 0
for enemy in maze.enemies:
# Check Chameleons
if getattr(enemy, 'visible', 1) <= 0 and maze.next_move <= 0:
continue
lines.append('{0} {2} {3} {1:.0f}'.format(
COLORS[enemy.get_color()], deg(enemy.angle),
*self.expos(*enemy.get_pos())))
ne += 1
for bullet in maze.bullets:
x, y = self.expos(bullet.x, bullet.y)
color, angle = COLORS[bullet.get_color()], deg(bullet.angle)
if color != '0':
lines.append('{} {} {} {:.0f}'.format(color, x, y, angle))
nb += 1
if walls: lines.appendleft('\n'.join(''.join(str(cell) for cell in row)
for row in walls))
lines.appendleft('{} {} {} {}'.format(len(walls), ne, nb,
maze.get_score()))
return '\n'.join(lines).encode()
def export_txt(self):
"""Export maze data to string."""
export = self.maze.update_export(forced=True)
return '{} {} {} {}\n{}{}{}{}'.format(
len(export['m']), len(export['e']), len(export['b']), export['s'],
''.join(row + '\n' for row in export['m']), join(export['h']),
''.join(map(join, export['e'])), ''.join(map(join, export['b'])))
def update(self):
"""Draw and handle meta events on Pygame window.
@ -191,12 +161,8 @@ class Game:
return False
elif event.type == VIDEORESIZE:
self.maze.resize((event.w, event.h))
elif event.type == KEYDOWN and not self.server:
if event.key == self.key['new']:
self.maze.reinit()
elif event.key == self.key['pause'] and not self.hero.dead:
self.paused ^= True
elif event.key == self.key['mute']:
elif event.type == KEYDOWN:
if event.key == self.key['mute']:
if pygame.mixer.get_init() is None:
pygame.mixer.init(frequency=44100)
pygame.mixer.music.load(MUSIC)
@ -204,6 +170,11 @@ class Game:
pygame.mixer.music.play(-1)
else:
pygame.mixer.quit()
elif not self.server:
if event.key == self.key['new']:
self.maze.reinit()
elif event.key == self.key['pause'] and not self.hero.dead:
self.paused ^= True
# Compare current FPS with the average of the last 10 frames
new_fps = self.clock.get_fps()
@ -269,7 +240,8 @@ class Game:
if self.hero.dead:
connection.send('0000000'.encode())
break
data = self.export()
data = self.export_txt().encode()
alpha = deg(self.hero.angle)
connection.send('{:07}'.format(len(data)).encode())
connection.send(data)
try:
@ -282,7 +254,9 @@ class Game:
except ValueError: # invalid input
break
y, x = (i - 1 for i in divmod(move, 3))
self.sockinp = x, y, radians(angle), attack & 1, attack >> 1
# Time is the essence.
angle = self.hero.angle if angle == alpha else radians(angle)
self.sockinp = x, y, angle, attack & 1, attack >> 1
clock.tick(self.fps)
self.sockinp = 0, 0, -pi * 3 / 4, 0, 0
new_time = get_ticks()
@ -329,6 +303,7 @@ class Game:
def __exit__(self, exc_type, exc_value, traceback):
if self.server is not None: self.server.close()
if not self.hero.dead: self.maze.dump_records()
pygame.quit()
@ -338,7 +313,7 @@ def main():
dirs = AppDirs(appname='brutalmaze', appauthor=False, multipath=True)
parents = dirs.site_config_dir.split(pathsep)
parents.append(dirs.user_config_dir)
filenames = [join(parent, 'settings.ini') for parent in parents]
filenames = [pathjoin(parent, 'settings.ini') for parent in parents]
config = ConfigReader(filenames)
config.parse()
@ -369,10 +344,19 @@ def main():
parser.add_argument(
'--music-volume', type=float, metavar='VOL', dest='musicvol',
help='between 0.0 and 1.0 (fallback: {})'.format(config.musicvol))
parser.add_argument('--space-music', action='store_true', dest='space',
default=None, help='use space music background')
parser.add_argument(
'--space-music', action='store_true', dest='space', default=None,
help='use space music background (fallback: {})'.format(config.space))
parser.add_argument('--default-music', action='store_false', dest='space',
help='use default music background')
parser.add_argument(
'--record-dir', metavar='DIR', dest='export_dir',
help='directory to write game records (fallback: {})'.format(
config.export_dir or '*disabled*'))
parser.add_argument(
'--record-rate', metavar='SPF', dest='export_rate',
help='snapshots of game state per second (fallback: {})'.format(
config.export_rate))
parser.add_argument(
'--server', action='store_true', default=None,
help='enable server (fallback: {})'.format(config.server))

View File

@ -31,7 +31,7 @@ Close-range attack: Space
[Record]
# Directory to write record of game states, leave blank to disable.
Directory: .
Directory:
# Number of snapshots per second. This is preferably from 3 to 60.
Frequency: 30

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -7,7 +7,7 @@ with open('README.rst') as f:
setup(
name='brutalmaze',
version='0.8.1',
version='0.8.20',
description='A minimalist TPS game with fast-paced action',
long_description=long_description,
url='https://github.com/McSinyx/brutalmaze',

2
wiki

@ -1 +1 @@
Subproject commit 1c771f4d4d258394614ad7a09ccaff9a6c756d76
Subproject commit 3c1a1a840b6b9959b676a3937df1099dcb24fc4c