鱼C论坛

 找回密码
 立即注册
查看: 5593|回复: 2

[技术交流] 《Dorfromantik》完美拼接脚本

[复制链接]
发表于 2021-9-10 11:23:26 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

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

x
本帖最后由 z412290894 于 2024-6-22 15:04 编辑

本人纯小白,刚跟着小甲鱼学习python几个月,目前看到第10章。
最近刚好在玩一款类似俄罗斯方块的游戏《Dorfromantik》,游戏里得高分的方法就是寻找最佳板块,刚开始用眼睛找,很累,然后学了python就想用脚本实现。
目前只写了大概300行,只简单实现了记录和寻找完美拼接的功能,后续会持续优化脚本,欢迎各位大佬指导指导!

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Dorfromantik
中文名:多罗曼蒂克

一、游戏介绍:
随机给玩家一个六边形板块,玩家根据自己的选择放置到地图上,从而组成独特的乡村风景。

六边形上的地形分为:
空地、房屋、农田、森林、河流、铁轨;
其中河流、铁轨只能和对应的板块连接,其他地形无特殊要求,可以随意连接

游戏没有时间限制,但是系统提供的板块有个数限制,放置完之后游戏结束!

每放置一个板块会有对应的分数,完成特殊的任务有板块奖励和额外分数,所以尽可能多的放置板块就可以获得更高的分数,组成的乡村也会更大!

二、游戏奖励机制:
1.开场系统给玩家40个板块,可以随意放置到地图上;
2.放置时六边形每有一边地形与旁边一致加10分,最多(六个方向的地形均一致)可加60分;
3.当一个六边形板块的六个地形均与旁边一致时,达成“完美拼接”,奖励一块新板块;(得高分的重点)
4.系统会随机给任务“地形图标加数字”,表示要求相同地形连续出现N次或N+次,达成目标后奖励5块板块,获得100分;(快速获得板块,优先级最高)
5.地图上随机出现旗帜,表示要求此地形形成封闭状态,达成目标后奖励5块板块;
6.成就系统,系统会给各种特殊的任务,完成后奖励分数和特殊板块(风车、水车、火车等);
7.隐藏地块,地图上未到达的地区会随机出现隐藏板块,当放置板块接近后会获得新的成就任务;

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
脚本见最新回复
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2022-9-11 18:51:05 | 显示全部楼层
感谢楼主,之前就想用代码实现完美拼接,没想到你已经做出来了,真厉害!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-6-22 14:49:54 | 显示全部楼层
代码已优化:
main.py

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. # @Auth :Ray
  4. """
  5. 此工具仅适用于《Dorfrmantik》(中文名:筑梦颂)游戏。
  6. 一、游戏介绍:
  7. 随机给玩家一个六边形板块,玩家根据自己的选择放置到地图上,从而组成独特的乡村风景。
  8. 六边形上的地形分为:
  9. 空地、房屋、农田、森林、铁路、河流
  10. 其中河流、铁路只能和对应的板块连接,其他地形无特殊要求,可以随意连接
  11. 游戏没有时间限制,但是系统提供的板块有个数限制,放置完之后游戏结束!
  12. 每放置一个板块会有对应的分数,完成特殊的任务有板块奖励和额外分数,所以尽可能多的放置板块就可以获得更高的分数,组成的乡村也会更大!

  13. 二、游戏奖励机制:
  14. 1.开场系统给玩家40个板块,可以随意放置到地图上;
  15. 2.放置时六边形每有一边地形与旁边一致加10分,最多(六个方向的地形均一致)可加60分;
  16. 3.当一个六边形板块的六个地形均与旁边一致时,达成“完美拼接”,奖励一块新板块;(得高分的重点)
  17. 4.系统会随机给任务“地形图标加数字”,表示要求相同地形连续出现N次或N+次,达成目标后奖励5块板块,获得100分;(快速获得板块,优先级最高)
  18. 5.地图上随机出现旗帜,表示要求此地形形成封闭状态,达成目标后奖励5块板块;
  19. 6.成就系统,系统会给各种特殊的任务,完成后奖励分数和特殊板块(风车、水车、火车等);
  20. 7.隐藏地块,地图上未到达的地区会随机出现隐藏板块,当放置板块接近后会获得新的成就任务;

  21. 三、游戏得分点:
  22. 1.当插入的地形与已有地形相同时,才能得10分,否则是0分;
  23. 2.当板块六个方向的地形均与相邻板块一致时,奖励60分和一个新板块;

  24. 四、工具实现内容:
  25. 1.记录原始板块的地形组合与顺序,保证与游戏内一致;(新游戏默认是一个六边空地)
  26. 2.用户输入下一个要插入板块的地形,计算出与之完美匹配的所有地形组合;
  27. 3.在原始地块中搜索并提示用户可以插入哪些位置;
  28. 4.用户输入实际插入板块的位置;
  29. 5.自动计算插入新板块后的地形组合及顺序;

  30. 五、工具优缺点:
  31. 优点:能保证每一次插入新板块都是最优解,比人工去找要方便很多,特别是后期大量板块需要人工比对;
  32. 缺点:操作费时费力,无法自动获取板块组成和插入位置,计算出来的最优地形太多,用户选择有困难
  33. """
  34. from plate import *
  35. import json


  36. if __name__ == '__main__':
  37.     # 游戏初始界面
  38.     welcome = eg.buttonbox(msg='Dorfromantik游戏开始,请选择是否是新游戏!', title='Dorfromantik Game Start!Welcome!',
  39.                            choices=['是', '不是'])

  40.     # 判断是否是新游戏,如果是新游戏则初始化板块
  41.     # 各个地形缩写:空地:K 房屋:F 农田:N 森林:S 铁路:T 河流:H
  42.     # plate_init_out元组代表外围一圈的地形可插入最优解地形,例如'KKF'表示此板块可以插入的地形为:‘空地+空地+房屋’
  43.     # plate_init_in集合代表内圈的地形和与之相关联的外圈地形,一般是五地形和六地形空板块,例如{'KKFKK': 'NXF'}
  44.     if welcome == '是':
  45.         plate_init_out = tuple('KKKKKK')    # 新游戏起始板块周边的空板块六边均为空地
  46.         plate_init_in = {}                  # 新游戏起始没有内圈空板块
  47.     else:
  48.         # 继续游戏时,从上次运行时保存的文件中读取外圈和内圈空板块数据
  49.         with open('plate_last_out.txt', 'r') as file_last_out:
  50.             plate_init_out = eval(file_last_out.read().strip())
  51.         with open('plate_last_in.json', 'r', encoding='utf-8') as file_last_in:
  52.             plate_init_in = json.load(file_last_in)

  53.     # 游戏循环开始

  54.     while True:
  55.         # 显示初始板块(废弃不用)
  56.         # file_plate_out(plate_init_out)  # 将初始化外部地形写入plate_out.txt
  57.         # with open('plate_out.txt') as file_1:
  58.         #     eg.textbox(msg='当前外圈板块为:\n(空地:K 房屋:F 农田:N 森林:S 铁路:T 河流:H)\n第一列:空白板块编号\n第二列:可适配地形', text=file_1.read(),
  59.         #                title='Dorfromantik Tool!')
  60.         # eg.msgbox(msg=f'当前内圈板块为:\n{plate_init_in}', title='Dorfromantik Tool!')

  61.         # 请用户输入下一个要插入的板块,逆时针输入
  62.         plate_next = enter_plate()

  63.         # 枚举新板块适合的所有板块地形
  64.         plate_fit = fit_plate(plate_next)

  65.         # 计算新板块最完美的拼接位置是哪里
  66.         best_plate = find_num(plate_fit, plate_init_out, plate_init_in)

  67.         # 请用户输入实际插入板块的位置是哪里
  68.         plate_num, new_num = enter_num(plate_init_out, plate_init_in, plate_next, best_plate)

  69.         # 显示插入新板块后的总地块
  70.         plate_next = tuple(plate_next)
  71.         plate_final = final_plate(plate_init_out, plate_init_in, plate_next, plate_num, new_num)

  72.         # 更新初始地块
  73.         plate_init_out = plate_final[0]
  74.         plate_init_in = copy.deepcopy(plate_final[2])
  75.         plate_init_in.update(plate_final[1])

  76.         # 用户确认插入后的结果是否正确
  77.         y_n_out = eg.ynbox(msg=f'插入新板块后外圈板块为:\n(空地:K 房屋:F 农田:N 森林:S 铁路:T 河流:H)\n{plate_init_out}', title='Dorfromantik Tool!')
  78.         y_n_in = eg.ynbox(msg=f'插入新板块后内圈板块为:\n(空地:K 房屋:F 农田:N 森林:S 铁路:T 河流:H)\n{plate_init_in}', title='Dorfromantik Tool!')
  79.         if not y_n_out or not y_n_in:
  80.             eg.msgbox(msg='游戏出错,请重新开始插入此板块!', title='Dorfromantik Tool!')
  81.             with open('plate_last_out.txt', 'r') as file_last_out:
  82.                 plate_init_out = eval(file_last_out.read().strip())
  83.             with open('plate_last_in.json', 'r', encoding='utf-8') as file_last_in:
  84.                 plate_init_in = json.load(file_last_in)
  85.             continue

  86.         # 备份上次游戏的结果
  87.         with open('plate_last_out.txt', 'w') as file_last_out:
  88.             file_last_out.write(repr(plate_init_out))
  89.         with open('plate_last_in.json', 'w', encoding='utf-8') as f:
  90.             json.dump(plate_init_in, f, ensure_ascii=False, indent=4)
复制代码



plate.py:

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. # @Auth :Ray
  4. import easygui as eg
  5. import re
  6. import copy


  7. def file_plate_out(plate_init_out):
  8.     """初始化板块"""
  9.     with open('plate_out.txt', 'w') as file_init:
  10.         for index, element in enumerate(plate_init_out):
  11.             file_init.write(f'{index}\t{element}\n')


  12. def enter_plate():
  13.     """新板块插入"""
  14.     eg_enter = eg.enterbox(msg='请输入下一个要插入的板块是(逆时针):\n(空地K 房屋F 农田N 森林S 铁路T 河流H)',
  15.                            title='Dorfromantik Tool!')
  16.     while True:
  17.         # 判断输入是否正确,否则重新要求用户输入
  18.         try:
  19.             if len(eg_enter) != 6 or not re.fullmatch(r'[KFNSTH]+', eg_enter):
  20.                 eg_enter = eg.enterbox(
  21.                     msg=f'输入的板块格式不对,请重新输入:\n(空地K 房屋F 农田N 森林S 铁路T 河流H)\n您上次输入的是:{eg_enter}',
  22.                     title='Dorfromantik Tool!')
  23.             else:
  24.                 break
  25.         except TypeError:
  26.             eg_enter = eg.enterbox(
  27.                 msg=f'输入的板块格式不对,请重新输入:\n(空地K 房屋F 农田N 森林S 铁路T 河流H)\n您上次输入的是:{eg_enter}',
  28.                 title='Dorfromantik Tool!')
  29.     return eg_enter


  30. def fit_plate(plate):
  31.     """枚举新板块适合的所有板块地形"""
  32.     """例如插入板块是'SKKKKK',适合的所有地形为:('K', 'KK', 'KKK', 'KKKK', 'KKKKK', 'KKKKKS', 'KKKKS', 'KKKKSK', 'KKKS',
  33.     'KKKSK', 'KKKSKK', 'KKS', 'KKSK', 'KKSKK', 'KKSKKK', 'KKXK', 'KKXS', 'KS', 'KSK', 'KSKK', 'KSKKK', 'KSKKKK',
  34.     'KSXK', 'KXK', 'KXKK', 'KXKS', 'KXS', 'KXSK', 'S', 'SK', 'SKK', 'SKKK', 'SKKKK', 'SKKKKK', 'SKXK', 'SXK', 'SXKK')"""
  35.     result = []
  36.     for i in range(0, len(plate)):
  37.         plate_1 = plate[i:] + plate[:i]
  38.         for j in range(1, len(plate_1) + 1):
  39.             result.append(plate_1[:j])
  40.         result.append(plate_1[:1] + 'X' + plate_1[2])
  41.         result.append(plate_1[:1] + 'X' + plate_1[2:4])
  42.         result.append(plate_1[:2] + 'X' + plate_1[3])
  43.     return tuple(sorted(list(set(result))))


  44. def find_num(plate_fit, plate_init_out, plate_init_in):
  45.     """计算哪些板块最适合插入"""
  46.     """与外圈、内圈板块比对,若一致则返回对应位置及板块名称"""
  47.     result = []
  48.     j = 6
  49.     while j > 0:
  50.         if j > 4:
  51.             for i in plate_fit:
  52.                 if len(i) == j:
  53.                     if i in plate_init_in:
  54.                         result.append({i: plate_init_in[i]})
  55.         else:
  56.             for i in plate_fit:
  57.                 if len(i) == j:
  58.                     for key, value in enumerate(plate_init_out):
  59.                         if value == i:
  60.                             result.append({key: value})
  61.         j -= 1
  62.     return result


  63. def enter_num(plate_init_out, plate_init_in, plate_next, best_plate):
  64.     """用户输入要插入新板块的位置"""
  65.     while True:
  66.         a = eg.enterbox(
  67.             msg=f'请输入在哪个位置插入新板块:\n(空地K 房屋F 农田N 森林S 铁路T 河流H)\n新板块是:{plate_next}\n建议插入位置:\n{best_plate}',
  68.             title='Dorfromantik Tool!')
  69.         try:
  70.             plate_num = int(a)
  71.             break
  72.         except (ValueError, TypeError):
  73.             if a in plate_init_in.keys():
  74.                 plate_num = a
  75.                 break
  76.             else:
  77.                 eg.msgbox(msg=f'输入无效,请重新输入要插入的位置!', title='Dorfromantik Tool!')

  78.     while True:
  79.         try:
  80.             if type(plate_num) is str:
  81.                 s = plate_num
  82.             else:
  83.                 s = plate_init_out[plate_num]
  84.             b = eg.enterbox(
  85.                 msg=f'请输入新板块要对接的位置:\n(空地K 房屋F 农田N 森林S 铁路T 河流H)\n待插入的地形是:{s}\n新板块是:{plate_next}',
  86.                 title='Dorfromantik Tool!')
  87.             new_num = int(b)
  88.             if 0 <= new_num <= 5:
  89.                 break
  90.             else:
  91.                 eg.msgbox(msg=f'超出新板块范围,请重新输入要对接的位置(0-5)!', title='Dorfromantik Tool!')
  92.                 continue
  93.         except (ValueError, TypeError):
  94.             eg.msgbox(msg=f'输入无效,请重新输入要对接的位置(0-5)!', title='Dorfromantik Tool!')
  95.     return plate_num, new_num


  96. def final_plate(plate_init_out, plate_init_in, plate_next, plate_num, new_num):
  97.     """计算插入后的板块"""
  98.     plate_finnal_out = []
  99.     plate_finnal_in = {}
  100.     plate_init_in_temp = copy.deepcopy(plate_init_in)

  101.     # 当待插入的地块为五地形和六地形时,plate_num为字符串    plate_init_in = {"KKSKK": "SXF"} SKKKKK
  102.     if type(plate_num) is str:
  103.         if len(plate_num) == 5:
  104.             k = plate_init_in[plate_num]  # SXF
  105.             plate_finnal_out = list(plate_init_out)
  106.             plate_finnal_out_1 = list(plate_init_out)
  107.             for key, value in enumerate(plate_finnal_out_1):
  108.                 if value == k:
  109.                     y_or_n = eg.ynbox(
  110.                         msg=f'当前地形为:\n{plate_finnal_out_1}\n请确认要插入五地形地块对应的{k}位置是否为{key}?',
  111.                         title='Dorfromantik Tool!')
  112.                     if y_or_n:
  113.                         plate_finnal_out[key] = plate_finnal_out_1[key].replace('X', plate_next[new_num - 1])
  114.             del plate_init_in[plate_num]
  115.         if len(plate_num) == 6:
  116.             del plate_init_in[plate_num]
  117.             plate_finnal_out = list(plate_init_out)

  118.     # 当待插入的地块为单地形到四地形时
  119.     else:
  120.         len_p = len(plate_init_out[plate_num])
  121.         if len_p in (1, 2, 3, 4):
  122.             # 当待插入的板块是带X时,对应的五地形板块需修改为六地形
  123.             if 'X' in plate_init_out[plate_num]:
  124.                 x_index = plate_init_out[plate_num].index('X')
  125.                 for key, value in plate_init_in.items():
  126.                     if value == plate_init_out[plate_num]:
  127.                         y_or_n = eg.ynbox(msg=f'请确认要插入的{plate_init_out[plate_num]}是否对应{key}?',
  128.                                           title='Dorfromantik Tool!')
  129.                         if y_or_n:
  130.                             key_new = key + plate_next[new_num + x_index - 6]
  131.                             del plate_init_in_temp[key]
  132.                             plate_init_in_temp[key_new] = None
  133.                 plate_init_in = copy.deepcopy(plate_init_in_temp)

  134.             if 'X' in plate_init_out[plate_num - len(plate_init_out) + 1]:
  135.                 for key, value in plate_init_in.items():
  136.                     if value == plate_init_out[plate_num+1]:
  137.                         y_or_n = eg.ynbox(msg=f'请确认要插入的{plate_init_out[plate_num + 1]}是否对应{key}?',
  138.                                           title='Dorfromantik Tool!')
  139.                         if y_or_n:
  140.                             value_new = (plate_next[new_num - 6 + len_p] + plate_init_out[plate_num - len(plate_init_out) + 1])
  141.                             plate_init_in_temp[key] = value_new
  142.                 plate_init_in = copy.deepcopy(plate_init_in_temp)

  143.             if 'X' in plate_init_out[plate_num - 1]:
  144.                 for key, value in plate_init_in.items():
  145.                     if value == plate_init_out[plate_num-1]:
  146.                         y_or_n = eg.ynbox(msg=f'请确认要插入的{plate_init_out[plate_num - 1]}是否对应{key}?',
  147.                                           title='Dorfromantik Tool!')
  148.                         if y_or_n:
  149.                             value_new = (plate_init_out[plate_num - 1] + plate_next[new_num - 1])
  150.                             plate_init_in_temp[key] = value_new
  151.                 plate_init_in = copy.deepcopy(plate_init_in_temp)

  152.             plate_finnal_out.append(plate_init_out[plate_num - 1] + plate_next[new_num - 1])
  153.             i = 2
  154.             while i < 6 - len_p:
  155.                 plate_finnal_out.append(plate_next[new_num - i])
  156.                 i += 1
  157.             plate_finnal_out.append(
  158.                 (plate_next[new_num - 6 + len_p] + plate_init_out[plate_num - len(plate_init_out) + 1]))
  159.             j = len(plate_init_out) - 2
  160.             while j > 1:
  161.                 plate_finnal_out.append(plate_init_out[plate_num - j])
  162.                 j -= 1

  163.     # 当plate_finnal中存在五地形和六地形时,需要特殊处理
  164.     for i in plate_finnal_out:
  165.         if len(i) == 5:
  166.             index_i = plate_finnal_out.index(i)
  167.             # 把五地形板块从地形列表中取出来,并将前后板块合并
  168.             plate_finnal_out.pop(index_i)
  169.             plate_finnal_out[index_i] = plate_finnal_out[index_i - 1] + 'X' + plate_finnal_out[index_i]
  170.             plate_finnal_out.pop(index_i - 1)
  171.             # 单独把五地形放入另一个字典里
  172.             if index_i != 0:
  173.                 plate_finnal_in[i] = plate_finnal_out[index_i - 1]
  174.             else:
  175.                 plate_finnal_in[i] = plate_finnal_out[index_i]
  176.     return tuple(plate_finnal_out), plate_finnal_in, plate_init_in
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-22 10:44

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表