查看: 16336|回复: 5

[原创发布] 带迷雾的大海战

[复制链接]
梦石
1
星屑
24088
在线时间
3339 小时
注册时间
2011-7-8
回帖
3862

开拓者

发表于 2021-4-18 00:53:31 | 显示全部楼层 |阅读模式
游戏发布
游戏类型:其他
版本信息:v1.0
制作工具:其他
游戏时长:0.1 小时
下载地址:

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
本帖最后由 guoxiaomi 于 2021-5-6 15:55 编辑

这是个概念性的游戏,尝试改进了经典游戏《大海战》,基本规则如下:

1. 双方各有10艘战舰,用 x 表示,分布在12x12的棋盘上、下两半
2. 每回合,双方各选1个坐标进行攻击。
3. 如果攻击直接命中了敌方战舰,该战舰会被击沉。
4. 如果攻击未命中,会尝试驱散3x3范围内的战争迷雾,移除战争迷雾的效果会逐渐消散。
5. 战舰本身也会制造战争迷雾,也会驱散对方的战争迷雾,并且多个战舰的效果会叠加。
6. 一开始全场都覆盖有战争迷雾(本底),此迷雾会逐渐消散。
7. 暴露给对方的战舰会立刻被摧毁。
8. 击毁全部敌方战舰获胜。

这个规则具体解释起来还是挺绕的,还是直接看源码比较快。
20210506更新:C++版本 battleship_v3.zip (26.27 KB, 下载次数: 111) ,也不知道能不能玩……

P0由玩家控制,P1由AI控制,AI没有作弊。

四张截图:
游戏开始,我方准备打击B2区域
Screenshot 2021-04-18 004554.png
我方F9区域战舰暴露,自动被击沉
Screenshot 2021-04-18 004656.png
双方战损近半,战况十分焦灼
Screenshot 2021-04-18 004931.png
游戏结束,我方仅剩2艘战舰存活
Screenshot 2021-04-18 005237.png

源码如下,可以去https://replit.com/@gxm11/battleshipv2?lite=true试玩,点击最上面的run即可:
[fold][pre lang="python"]# ship battle game
# 1. ships can generate a detect value to every block nearby, v = 1 / (dx * dx + dy * dy)
# 2. the sea has its own detect limit, which will decrease slightly
# 3. attack will add a temp detect value to 3x3 blocks
# 4. any block where detect value * 2 + temp value > sum(detect value) + detect limit will be visible
import numpy as np
import re
import string
import sys


class Ship_Battle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

        def kernel_r2(dx, dy):
            return 1 / (1 + dx * dx + dy * dy)

        self.kernel = self.kernel_cache(kernel_r2)

        def kernel_3x3(dx, dy):
            if dx == 0 and dy == 0:
                return 8
            if abs(dx) <= 1 and abs(dy) <= 1:
                return 1
            return 0

        self.attack = {"normal": self.kernel_cache(kernel_3x3)}

    def kernel_cache(self, kernel):
        ret = np.zeros((self.width * 2, self.height * 2))
        for i in range(self.width * 2):
            for j in range(self.height * 2):
                ret[i, j] = kernel(i - self.width, j - self.height)
        return ret

    def reset(self):
        self.turn = 0
        self.water_level = 1
        self.temp_value = np.zeros((self.width, self.height, 2))

        self.ships = self.create_ships(10)
        self.broken_ships = []

    def winner(self):
        for i in [0, 1]:
            if len(self.ships) == 0:
                return 1 - i
        return None

    def create_ships(self, n):
        # p0's ship is in 0 ~ w-1  h/2 ~ h-1
        pos = np.arange(self.width * self.height / 2, dtype=int)
        np.random.shuffle(pos)
        ships_0 = []
        for i in pos[0:n]:
            _x, _y = i % self.width, i // self.width + self.height // 2
            ships_0.append((_x, _y))

        # p1's ship is in 0 ~ w-1  0 ~ h/2-1
        pos = np.arange(self.width * self.height / 2, dtype=int)
        np.random.shuffle(pos)
        ships_1 = []
        for i in pos[0:n]:
            _x, _y = i % self.width, i // self.width
            ships_1.append((_x, _y))

        return (ships_0, ships_1)

    def update(self):
        for i, ships in enumerate(self.ships):
            value = self.value_int(1 - i)
            for _x, _y in ships:
                if value[_x, _y] == 1:
                    ships.remove((_x, _y))
                    self.broken_ships.append((_x, _y))

        self.turn += 1
        self.water_level = self.water_level * 0.9
        self.temp_value = self.temp_value * 0.9

    def action(self, a):
        pid, x, y, kind = a
        x0, y0 = self.width - x, self.height - y
        dv = self.attack[kind][x0:x0 + self.width, y0:y0 + self.height]
        self.temp_value[:, :, pid] += dv

    def calculate_value(self):
        values = np.zeros((self.width, self.height, 2))
        for i, ships in enumerate(self.ships):
            v = np.zeros((self.width, self.height))
            for _x, _y in ships:
                x0, y0 = self.width - _x, self.height - _y
                v += self.kernel[x0:x0 + self.width, y0:y0 + self.height]
            values[:, :, i] = v
        return values

    def value_int(self, pid):
        ret = np.zeros((self.width, self.height), dtype=int)
        value = self.calculate_value()
        v0 = value[:, :, pid] * 2 + self.temp_value[:, :, pid]
        v1 = np.sum(value, axis=2) + self.water_level
        ret[v0 >= v1] = 1
        return ret

    def render(self, pid=0):
        template = "T%-2d"
        for i in range(self.width):
            template += "%2s" % string.ascii_uppercase
        for j in range(self.height):
            template += "\n"
            template += "%3d" % (j + 1)
            for i in range(self.width):
                template += "%2s"

        vars = [self.turn]
        value = self.value_int(pid)

        for j in range(self.width):
            for i in range(self.height):
                if (i, j) in self.broken_ships:
                    c = "#"
                elif (i, j) in self.ships[pid]:
                    c = "x"
                else:
                    if value[i, j] == 1:
                        if (i, j) in self.ships[1 - pid]:
                            c = "!"
                        else:
                            c = " "
                    else:
                        c = "."

                vars.append(c)

        print(template % tuple(vars))

    def render_action(self, a, pid=0):
        value = self.value_int(pid)
        if value[a[1], a[2]] == 1:
            t = (a[0], string.ascii_uppercase[a[1]], a[2] + 1)
            print("P%d attack %s%d" % t)
        else:
            print("P%d attack ?" % a[0])


class Game:
    def __init__(self, game):
        self.game = game

    def run(self):
        g = self.game
        g.reset()
        g.render(0)
        self.cpu = None
        while g.winner() is None:
            a0 = None
            while a0 is None:
                a0 = self.human_action(g)
            a1 = self.cpu_action(g)
            g.action(a0)
            g.action(a1)
            g.update()
            g.render_action(a0)
            g.render_action(a1)
            g.render(0)
        print("winner is P%d" % g.winner())

    def run_test(self):
        g = self.game
        w = [0, 0, 0]
        for i in range(1000):
            g.reset()
            self.cpu = None
            while g.winner() is None:
                a = self.human_action_test(g)
                a = self.cpu_action(g)
                g.action(a)
                g.action(a)
                g.update()
            w[g.winner()] += 1
            w[2] += g.turn
        w[2] = w[2] / (w[0] + w[1])
        print("P0 win: %d, P1 win %d, Mean Turns = %.2f" % tuple(w))

    def human_action(self, game):
        str = input("[T%d] Type pos to attack: " % game.turn)
        m = re.match(r"^(\w)(\d+)$", str)
        if m is None:
            return
        if m.group(1) in string.ascii_uppercase:
            x = ord(str[0]) - ord('A')
        else:
            x = ord(str[0]) - ord('a')
        y = int(m.group(2)) - 1
        return (0, x, y, "normal")

    def human_action_test(self, game):
        _x = np.random.randint(game.width)
        _y = np.random.randint(game.height // 2)
        return (0, _x, _y, "normal")

    def cpu_action(self, game):
        if self.cpu is None:
            pos = []
            for i in range(game.width * game.height // 2):
                _x, _y = i % game.width, i // game.width + game.height // 2
                pos.append((_x, _y))
            self.cpu = [pos, None]

        pos, last = self.cpu
        value = game.value_int(1)
        for i, j in pos:
            if value[i, j] == 1:
                pos.remove((i, j))
        if len(pos) > 0:
            ix = np.random.randint(len(pos))
            action_x, action_y = pos[ix]
        else:
            _x = np.random.randint(game.width)
            _y = np.random.randint(game.height)
            return (1, _x, _y, "normal")

        if last is not None:
            x, y = last
            x_shift, y_shift = 0, 0

            for i in [-1, 0, 1]:
                for j in [-1, 0, 1]:
                    if 0 <= x + i < game.width and 0 <= y + j < game.height:
                        if value[x + i, y + j] == 0:
                            x_shift += i
                            y_shift += j
            x_shift = int((x_shift + 0.5) // 1.5)
            y_shift = int((y_shift + 0.5) // 1.5)
            _x = x + x_shift
            _y = y + y_shift
            if (_x, _y) in pos:
                action_x, action_y = _x, _y

        self.cpu[0].remove((action_x, action_y))
        self.cpu[1] = (action_x, action_y)
        return (1, action_x, action_y, "normal")


if __name__ == "__main__":
    if len(sys.argv) == 2:
        if sys.argv[1] == "test":
            g = Game(Ship_Battle(12, 12))
            g.run_test()
            exit()

    g = Game(Ship_Battle(12, 12))
    while True:
        g.run()
[/pre][/fold]

评分

参与人数 1赞 +1 收起 理由
赤炎 + 1

查看全部评分

熟悉rgss和ruby,xp区版主~
正在填坑:《膜拜组传奇》讲述膜拜组和学霸们的故事。
已上steam:与TXBD合作的Reformers《变革者》
* 战斗调用公共事件 *
* RGSOS 网络脚本 *
梦石
1
星屑
175
在线时间
17 小时
注册时间
2017-4-24
回帖
9
发表于 2021-4-18 04:06:15 | 显示全部楼层
前排!

评分

参与人数 1赞 +1 收起 理由
guoxiaomi + 1 塞糖

查看全部评分

回复

使用道具 举报

梦石
0
星屑
999
在线时间
73 小时
注册时间
2021-4-25
回帖
63
发表于 2021-4-25 17:05:24 | 显示全部楼层
你………………

评分

参与人数 1赞 +1 收起 理由
guoxiaomi + 1 塞糖

查看全部评分

回复

使用道具 举报

梦石
8
星屑
2872
在线时间
476 小时
注册时间
2010-9-11
回帖
480
发表于 2021-4-25 19:49:54 | 显示全部楼层
感觉好有趣!哪天我也做一个试试

评分

参与人数 1赞 +1 收起 理由
guoxiaomi + 1 塞糖

查看全部评分

Paku
回复

使用道具 举报

梦石
0
星屑
12824
在线时间
1010 小时
注册时间
2019-11-5
回帖
2329

极短27参与极短25参与极短24参与

发表于 2021-4-25 22:43:08 | 显示全部楼层
感觉拓宽了游戏的界限233

点评

猜硬币正反也是游戏,而且规模可以大到全世界一起玩:https://zhuanlan.zhihu.com/p/24071884  发表于 2021-4-26 00:40
风继续吹,不忍远离。年少轻狂,眼神明亮,最好的年纪,最好的时光。希望岁月是一场春梦
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部