人工智能--打飞机游戏

代码下载:Here。

很久以前微信流行过一个小游戏:打飞机,这个游戏简单又无聊。在2017年来临之际,我就实现一个超级弱智的人工智能(AI),这货可以躲避从屏幕上方飞来的飞机。本帖只使用纯Python实现,不依赖任何高级库。

本文的AI基于neuro-evolution,首先简单科普一下neuro-evolution。从neuro-evolution这个名字就可以看出它由两部分组成-neuro and evolution,它是使用进化算法(遗传算法是进化算法的一种)提升人工神经网络的机器学习技术,其实就是用进化算法改进并选出最优的神经网络。

使用TPOT自动选择scikit-learn机器学习模型和参数
neuro-evolution

定义一些变量:

import math
import random

神经网络3层, 1个隐藏层; 4个input和1个output

network = [4, [16], 1]

遗传算法相关

population = 50
elitism = 0.2
random_behaviour = 0.1
mutation_rate = 0.5
mutation_range = 2
historic = 0
low_historic = False
score_sort = -1
n_child = 1
定义神经网络:

激活函数

def sigmoid(z):
return 1.0/(1.0+math.exp(-z))

random number

def random_clamped():
return random.random()*2-1

“神经元”

class Neuron():
def init(self):
self.biase = 0
self.weights = []

def init_weights(self, n):
    self.weights = []
    for i in range(n):
        self.weights.append(random_clamped())
def __repr__(self):
    return 'Neuron weight size:{}  biase value:{}'.format(len(self.weights), self.biase)

class Layer():
def init(self, index):
self.index = index
self.neurons = []

def init_neurons(self, n_neuron, n_input):
    self.neurons = []
    for i in range(n_neuron):
        neuron = Neuron()
        neuron.init_weights(n_input)
        self.neurons.append(neuron)

def __repr__(self):
    return 'Layer ID:{}  Layer neuron size:{}'.format(self.index, len(self.neurons))

神经网络

class NeuroNetwork():
def init(self):
self.layers = []

# input:输入层神经元数 hiddens:隐藏层 output:输出层神经元数
def init_neuro_network(self, input, hiddens , output):
    index = 0
    previous_neurons = 0
    # input
    layer = Layer(index)
    layer.init_neurons(input, previous_neurons)
    previous_neurons = input
    self.layers.append(layer)
    index += 1
    # hiddens
    for i in range(len(hiddens)):
        layer = Layer(index)
        layer.init_neurons(hiddens[i], previous_neurons)
        previous_neurons = hiddens[i]
        self.layers.append(layer)
        index += 1
    # output
    layer = Layer(index)
    layer.init_neurons(output, previous_neurons)
    self.layers.append(layer)

def get_weights(self):
    data = { 'network':[], 'weights':[] }
    for layer in self.layers:
        data['network'].append(len(layer.neurons))
        for neuron in layer.neurons:
            for weight in neuron.weights:
                data['weights'].append(weight)
    return data

def set_weights(self, data):
    previous_neurons = 0
    index = 0
    index_weights = 0

    self.layers = []
    for i in data['network']:
        layer = Layer(index)
        layer.init_neurons(i, previous_neurons)
        for j in range(len(layer.neurons)):
            for k in range(len(layer.neurons[j].weights)):
                layer.neurons[j].weights[k] = data['weights'][index_weights]
                index_weights += 1
        previous_neurons = i
        index += 1
        self.layers.append(layer)

# 输入游戏环境中的一些条件(如敌机位置), 返回要执行的操作
def feed_forward(self, inputs):
    for i in range(len(inputs)):
        self.layers[0].neurons[i].biase = inputs[i]

    prev_layer = self.layers[0]
    for i in range(len(self.layers)):
        # 第一层没有weights
        if i == 0:
            continue
        for j in range(len(self.layers[i].neurons)):
            sum = 0
            for k in range(len(prev_layer.neurons)):
                sum += prev_layer.neurons[k].biase * self.layers[i].neurons[j].weights[k]
            self.layers[i].neurons[j].biase = sigmoid(sum)
        prev_layer = self.layers[i]

    out = []
    last_layer = self.layers[-1]
    for i in range(len(last_layer.neurons)):
        out.append(last_layer.neurons[i].biase)
    return out

def print_info(self):
    for layer in self.layers:
        print(layer)

遗传算法:

“基因组”

class Genome():
def init(self, score, network_weights):
self.score = score
self.network_weights = network_weights

class Generation():
def init(self):
self.genomes = []

def add_genome(self, genome):
    i = 0
    for i in range(len(self.genomes)):
        if score_sort < 0:
            if genome.score > self.genomes[i].score:
                break
        else:
            if genome.score < self.genomes[i].score:
                break
    self.genomes.insert(i, genome)

    # 杂交+突变
def breed(self, genome1, genome2, n_child):
    datas = []
    for n in range(n_child):
        data = genome1
        for i in range(len(genome2.network_weights['weights'])):
            if random.random() <= 0.5:
                data.network_weights['weights'][i] = genome2.network_weights['weights'][i]

        for i in range(len(data.network_weights['weights'])):
            if random.random() <= mutation_rate:
                data.network_weights['weights'][i] += random.random() * mutation_range * 2 - mutation_range
        datas.append(data)
    return datas

    # 生成下一代
def generate_next_generation(self):
    nexts = []
    for i in range(round(elitism*population)):
        if len(nexts) < population:
            nexts.append(self.genomes[i].network_weights)

    for i in range(round(random_behaviour*population)):
        n = self.genomes[0].network_weights
        for k in range(len(n['weights'])):
            n['weights'][k] = random_clamped()
        if len(nexts) < population:
            nexts.append(n)

    max_n = 0
    while True:
        for i in range(max_n):
            childs = self.breed(self.genomes[i], self.genomes[max_n], n_child if n_child > 0 else 1)
            for c in range(len(childs)):
                nexts.append(childs[c].network_weights)
                if len(nexts) >= population:
                    return nexts
        max_n += 1
        if max_n >= len(self.genomes)-1:
            max_n = 0

NeuroEvolution:
class Generations():
def init(self):
self.generations = []

def first_generation(self):
    out = []
    for i in range(population):
        nn = NeuroNetwork()
        nn.init_neuro_network(network[0], network[1], network[2])
        out.append(nn.get_weights())
    self.generations.append(Generation())
    return out

def next_generation(self):
    if len(self.generations) == 0:
        return False

    gen = self.generations[-1].generate_next_generation()
    self.generations.append(Generation())
    return gen

def add_genome(self, genome):
    if len(self.generations) == 0:
        return False

    return self.generations[-1].add_genome(genome)

class NeuroEvolution():
def init(self):
self.generations = Generations()

def restart(self):
    self.generations = Generations()

def next_generation(self):
    networks = []
    if len(self.generations.generations) == 0:
        networks = self.generations.first_generation()
    else:
        networks = self.generations.next_generation()

    nn = []
    for i in range(len(networks)):
        n = NeuroNetwork()
        n.set_weights(networks[i])
        nn.append(n)

    if low_historic:
        if len(self.generations.generations) >= 2:
            genomes = self.generations.generations[len(self.generations.generations) - 2].genomes
            for i in range(genomes):
                genomes[i].network = None

    if historic != -1:
        if len(self.generations.generations) > historic+1:
            del self.generations.generations[0:len(self.generations.generations)-(historic+1)]

    return nn

def network_score(self, score, network):
    self.generations.add_genome(Genome(score, network.get_weights()))

是AI就躲个飞机

import pygame
import sys
from pygame.locals import *
import random
import math

import neuro_evolution

BACKGROUND = (200, 200, 200)
SCREEN_SIZE = (320, 480)

class Plane():
def init(self, plane_image):
self.plane_image = plane_image
self.rect = plane_image.get_rect()

    self.width = self.rect[2]
    self.height = self.rect[3]
    self.x = SCREEN_SIZE[0]/2 - self.width/2
    self.y = SCREEN_SIZE[1] - self.height

    self.move_x = 0
    self.speed = 2

    self.alive = True

def update(self):
    self.x += self.move_x * self.speed

def draw(self, screen):
    screen.blit(self.plane_image, (self.x, self.y, self.width, self.height))

def is_dead(self, enemes):
    if self.x < -self.width or self.x + self.width > SCREEN_SIZE[0]+self.width:
        return True

    for eneme in enemes:
        if self.collision(eneme):
            return True
    return False

def collision(self, eneme):
    if not (self.x > eneme.x + eneme.width or self.x + self.width < eneme.x or self.y > eneme.y + eneme.height or self.y + self.height < eneme.y):
        return True
    else:
        return False

def get_inputs_values(self, enemes, input_size=4):
    inputs = []

    for i in range(input_size):
        inputs.append(0.0)

    inputs[0] = (self.x*1.0 / SCREEN_SIZE[0])
    index = 1
    for eneme in enemes:
        inputs[index] = eneme.x*1.0 / SCREEN_SIZE[0]
        index += 1
        inputs[index] = eneme.y*1.0 / SCREEN_SIZE[1]
        index += 1
    #if len(enemes) > 0:
        #distance = math.sqrt(math.pow(enemes[0].x + enemes[0].width/2 - self.x + self.width/2, 2) + math.pow(enemes[0].y + enemes[0].height/2 - self.y + self.height/2, 2));
    if len(enemes) > 0 and self.x < enemes[0].x:
        inputs[index] = -1.0
        index += 1
    else:
        inputs[index] = 1.0

    return inputs

class Enemy():
def init(self, enemy_image):
self.enemy_image = enemy_image
self.rect = enemy_image.get_rect()

    self.width = self.rect[2]
    self.height = self.rect[3]
    self.x = random.choice(range(0, int(SCREEN_SIZE[0] - self.width/2), 71))
    self.y = 0

def update(self):
    self.y += 6

def draw(self, screen):
    screen.blit(self.enemy_image, (self.x, self.y, self.width, self.height))

def is_out(self):
    return True if self.y >= SCREEN_SIZE[1] else False

class Game():
def init(self):
pygame.init()
self.screen = pygame.display.set_mode(SCREEN_SIZE)
self.clock = pygame.time.Clock()
pygame.display.set_caption(‘是AI就躲个飞机’)

    self.ai = neuro_evolution.NeuroEvolution()
    self.generation = 0

    self.max_enemes = 1
            # 加载飞机、敌机图片
    self.plane_image = pygame.image.load('plane.png').convert_alpha()
    self.enemy_image = pygame.image.load('enemy.png').convert_alpha()

def start(self):
    self.score = 0
    self.planes = []
    self.enemes = []

    self.gen = self.ai.next_generation()
    for i in range(len(self.gen)):
        plane = Plane(self.plane_image)
        self.planes.append(plane)

    self.generation += 1
    self.alives = len(self.planes)

def update(self, screen):
    for i in range(len(self.planes)):
        if self.planes[i].alive:
            inputs = self.planes[i].get_inputs_values(self.enemes)
            res = self.gen[i].feed_forward(inputs)
            if res[0] < 0.45:
                self.planes[i].move_x = -1
            elif res[0] > 0.55:
                self.planes[i].move_x = 1


            self.planes[i].update()
            self.planes[i].draw(screen)

            if self.planes[i].is_dead(self.enemes) == True:
                self.planes[i].alive = False
                self.alives -= 1
                self.ai.network_score(self.score, self.gen[i])
                if self.is_ai_all_dead():
                    self.start()


    self.gen_enemes()

    for i in range(len(self.enemes)):
        self.enemes[i].update()
        self.enemes[i].draw(screen)
        if self.enemes[i].is_out():
            del self.enemes[i]
            break

    self.score += 1

    print("alive:{}, generation:{}, score:{}".format(self.alives, self.generation, self.score), end='\r')

def run(self, FPS=1000):
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

        self.screen.fill(BACKGROUND)

        self.update(self.screen)

        pygame.display.update()
        self.clock.tick(FPS)

def gen_enemes(self):
    if len(self.enemes) < self.max_enemes:
        enemy = Enemy(self.enemy_image)
        self.enemes.append(enemy)

def is_ai_all_dead(self):
    for plane in self.planes:
        if plane.alive:
            return False
    return True

game = Game()
game.start()
game.run()

AI的工作逻辑

假设你是AI,你首先繁殖一个种群(50个个体),开始的个体大都是歪瓜裂枣(上来就被敌机撞)。但是,即使是歪瓜裂枣也有表现好的,在下一代,你会使用这些表现好的再繁殖一个种群,经过代代相传,存活下来的个体会越来越优秀。其实就是仿达尔文进化论,种群->自然选择->优秀个体->杂交、变异->种群->循环n世代。

ai开始时候的表现:

是AI就躲个飞机 - 纯Python实现人工智能图片被拉扁了 sorry
经过几百代之后,ai开始娱乐的躲飞机:

是AI就躲个飞机 - 纯Python实现人工智能

ps.祝大家新年快乐,感觉我又浪费了一年

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值