123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- #!/usr/local/bin/python3
- # -*- coding: utf-8 -*-
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- import torch.utils.data as tudata
- import numpy as np
- import sys
- import json
- import os
- #将数据输出到一个json文件,便于在tiled进行观察,可以是单层或者多层的,多层的话,层级按照从小到大排列(层级是布局层级,不是视觉层级)
- def output_to_json(slices, suffix="ex"):
- with open("/Users/xulianxin/Documents/develop/game/TileMatch/TileManor.Lv/TileManor/templates/tm_0000.json", 'r') as f:
- json_temp = json.load(f)
- for i in range(0, len(slices)):
- with open("/Users/xulianxin/Documents/develop/game/TileMatch/TileManor.Lv/TileManor/slices/slice_%d_%s.json" % (i, suffix), "w") as out_f:
- json_temp["layers"][0]["data"] = slices[i]
- json.dump(json_temp, out_f)
- # 解析一个关卡文件,得到各层的数据,是按照视觉层次划分的
- def get_each_view_slice(jsonLv):
- jsonData = ""
- tilesByPos = {}
- maxZ = 0
- # 处理各层的tile数据
- with open(jsonLv, 'r') as f:#, encoding='utf-8'
- jsonData = json.load(f)
- # 得到宽高信息
- w = int(jsonData['width'])
- h = int(jsonData['height'])
- for item in jsonData['layers']:
- name = item['name']
- if not name.startswith('Tile_'):
- # stacked
- continue
- z = int(name[5:])
- data = item['data']
- for i in range(0, len(data)):
- if data[i] == 0:
- continue
- tile_x = (int)(i % w)
- tile_y = (int)(i / h)
- tile_z = z
- # 一些统计信息
- tilesByPos[(tile_x, tile_y, tile_z)] = [1,0] # [tile类型,zView--视觉层级,默认是0]
- if tile_z > maxZ:
- maxZ = tile_z
- # 根据上面的布局信息,计算每个tile的几个信息:
- # 1. 每个tile的视觉层级(不同于上面z的信息,那是一个布局信息,相同的z可能出在不同的视觉层级)
- # 从maxZ开始,逐层向下计算;对于每一个位置,如果该位置有tile,则计算该tile的视觉层级
- # 对于每一个tile,如果其上方(从该tile的z+1层,一直到maxZ层)有tile,则其视觉层级为上方tile的视觉层级+1
- slice_by_view_z = [[0]*w*h for i in range(maxZ+1)]
- for z in range(0, maxZ+1):
- z = maxZ - z
- for x in range(0, w+1):
- for y in range(0, h+1):
- if (x,y,z) in tilesByPos:
- tile = tilesByPos[(x, y, z)]
- adjs = [(x,y),(x,y+1),(x,y-1),(x+1,y),(x+1,y+1),(x+1,y-1),(x-1,y),(x-1,y-1),(x-1,y+1)]
- # 确定视觉层级
- zvMax = -1
- for zup in range(z+1, maxZ+1):
- for adj in adjs:
- if (adj[0],adj[1],zup) in tilesByPos:
- zv = tilesByPos[adj[0],adj[1],zup][1]
- if zv > zvMax:
- zvMax = zv
- zv = zvMax + 1
- tilesByPos[(x, y, z)][1] = zv
- # 记录到slice_by_view_z中
- slice_by_view_z[zv][y*w+x] = tilesByPos[(x, y, z)][0]
- return slice_by_view_z
- # 测试获取视觉层级的数据是否ok
- def test_get_each_view_slice():
- jsonLv = "/Users/xulianxin/Documents/develop/game/TileMatch/TileManor.Lv/TileManor/templates/tm_0016.json"
- slice_by_view_z = get_each_view_slice(jsonLv)
- output_to_json(slice_by_view_z)
- ##########################################
- # PyTorch RNN
- class TileMatchRNN(nn.Module):
- def __init__(self, input_size, hidden_size, num_layers, output_size):
- super(TileMatchRNN, self).__init__()
- self.hidden_size = hidden_size
- self.num_layers = num_layers
- self.rnn = nn.GRU(input_size, hidden_size, num_layers, batch_first=True)
- self.fc = nn.Linear(hidden_size, output_size)
- # 检查是否有可用的GPU
- self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
- def forward(self, x):
- # Add an extra dimension for the batch size if necessary
- if x.dim() == 2:
- x = x.unsqueeze(0)
- out, h = self.rnn(x)
- out = self.fc(out[:, -1, :])
- return out, h
-
- # 全连接神经网络
- class TileMatchFullyConnectedNetwork(nn.Module):
- def __init__(self, input_size, hidden_size, num_layers, output_size):
- super(TileMatchFullyConnectedNetwork, self).__init__()
- self.layer1 = nn.Linear(input_size, hidden_size)
- self.layer2 = nn.Linear(hidden_size, output_size)
- # 检查是否有可用的GPU
- self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
- def forward(self, x):
- x = torch.relu(self.layer1(x))
- x = self.layer2(x)
- return x
-
- # 变分自编码器
- class TileMatchVAE(nn.Module):
- def __init__(self, input_size, hidden_size, latent_size):
- super(TileMatchVAE, self).__init__()
- self.encoder = nn.Sequential(
- nn.Linear(input_size, hidden_size),
- nn.ReLU(),
- nn.Linear(hidden_size, latent_size * 2) # We need mean and variance for each latent variable
- )
- self.decoder = nn.Sequential(
- nn.Linear(latent_size, hidden_size),
- nn.ReLU(),
- nn.Linear(hidden_size, input_size),
- nn.Sigmoid() # To get outputs in the range [0, 1]
- )
- def reparameterize(self, mu, logvar):
- std = torch.exp(0.5 * logvar)
- eps = torch.randn_like(std)
- return mu + eps * std
- def forward(self, x):
- h = self.encoder(x)
- mu, logvar = h.chunk(2, dim=1)
- z = self.reparameterize(mu, logvar)
- return self.decoder(z), mu, logvar
- class TileMatchDataset(tudata.Dataset):
- def __init__(self, directories):
- self.data = []
- for directory in directories:
- for filename in os.listdir(directory):
- filepath = os.path.join(directory, filename)
- if os.path.isfile(filepath) and filepath.endswith('.json'):
- slices = get_each_view_slice(filepath)
- if len(slices) > 1:
- for i in range(0, len(slices)-1):
- self.data.append([slices[i], slices[i+1]])
- def __len__(self):
- return len(self.data)
- def __getitem__(self, idx):
- input_sequence = self.data[idx][0]
- target_sequence = self.data[idx][1]
- return torch.tensor(input_sequence, dtype=torch.float32), torch.tensor(target_sequence, dtype=torch.float32)
- # 模型的参数
- input_size = 900
- hidden_size = 50
- num_layers = 2
- output_size = 900
- def train_NN():
- # Initialize model, loss function, optimizer
- # model = TileMatchRNN(input_size, hidden_size, num_layers, output_size)
- # criterion = nn.BCEWithLogitsLoss() # 适合二分类问题
- # optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
- model = TileMatchVAE(input_size, 500, 20)
- criterion = nn.BCEWithLogitsLoss() # 适合二分类问题
- optimizer = torch.optim.SGD(model.parameters(), lr=0.001)
- # 准备数据集
- batch_size = 1
- directories = ["/Users/xulianxin/Documents/develop/game/TileMatch/TileManor.Lv/TileManor/tf_templates",]
- dataset = TileMatchDataset(directories)
- train_loader = tudata.DataLoader(dataset, batch_size=batch_size, shuffle=True)
- num_epochs = 10
- for epoch in range(num_epochs):
- for data in train_loader: # Assume train_loader provides (input, target) pairs
- inputs, targets = data
- # outputs, _ = model(inputs)
- x_recon, mu, logvar = model(inputs)
- recon_loss = F.binary_cross_entropy(x_recon, inputs, reduction='sum')
- kl_div = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
- loss = recon_loss + kl_div
- optimizer.zero_grad()
- loss.backward()
- optimizer.step()
- return model
- if __name__ == '__main__':
- # test_get_each_view_slice()
- n = train_NN()
- torch.save(n, '/Users/xulianxin/Documents/develop/game/TileMatch/TileManor.Lv/TileManor/nn_VAE.model')
- # n = torch.load('/Users/xulianxin/Documents/develop/game/TileMatch/TileManor.Lv/TileManor/nn_VAE.model')
- slices = get_each_view_slice("/Users/xulianxin/Documents/develop/game/TileMatch/TileManor.Lv/TileManor/templates/tm_0015.json")
- n.eval()
- # 准备输入数据
- initial_input = torch.tensor(slices[0], dtype=torch.float32).unsqueeze(0) # (1, 1, 900)
- # 初始化隐藏状态
- h0 = torch.zeros(num_layers, initial_input.size(0), hidden_size)
- # 使用模型进行预测 生成6层
- slices = []
- for i in range(1):
- with torch.no_grad(): # 在评估模式下,不需要计算梯度
- # output, hn = n(initial_input)
- output, mu, logvar = n(initial_input)
- initial_input = output
- # 应用阈值判断将浮点数转化为0或1
- threshold = 0.9
- output_layout = (torch.sigmoid(output) >= threshold).int()
- # 将 output_layout 转化成一个[]
- # slices.append([int(d) for d in output_layout[0]])
- slices.append([int(d) for d in output_layout[0]])
- output_to_json(slices, "nn_vae")
|