-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
184 lines (150 loc) · 6.38 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# python 3.6.4
import random
import time
import heapq
# create ASCII versions of images with pillow?
random.seed(time.time())
all_chars = ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
'0123456789 ,.:;!@#$%^&*()-_=+<>/?[]}{|~')
target = input("Target string?\n")
class Generation():
def __init__(self, size=None, prev=None):
self.individuals = []
if size is not None:
for i in range(size):
if len(self.males) == 0:
self.individuals.append(Individual(gender='Male'))
elif len(self.females) == 0:
self.individuals.append(Individual(gender='Female'))
else:
self.individuals.append(Individual())
elif prev is not None:
parent_count = len(prev.individuals) // 20
parents = {'Male': [], 'Female': []}
for gender in parents:
parents[gender] = prev.best_individuals(parent_count, gender)
best = prev.best_individuals(1)[0]
second = prev.best_individuals(2)[1]
gen_growth = second.score // best.score
self.breed(parents, len(prev.individuals) + gen_growth)
@property
def males(self):
return [indv for indv in self.individuals if indv.gender == 'Male']
@property
def females(self):
return [indv for indv in self.individuals if indv.gender == 'Female']
def segregated(self, gender):
if gender == 'Male':
return self.males
else:
return self.females
def breed(self, specimens_by_gender, pop_size):
for i in range(pop_size):
male = random.choice(specimens_by_gender['Male'])
female = random.choice(specimens_by_gender['Female'])
if len(self.males) == 0:
self.individuals.append(Individual(male,
female,
gender='Male'))
elif len(self.females) == 0:
self.individuals.append(Individual(male,
female,
gender='Female'))
else:
self.individuals.append(Individual(male,
female))
return
def best_individuals(self, n, gender=None):
if gender is not None:
return heapq.nsmallest(n,
self.segregated(gender),
key=lambda indv: indv.score)
return heapq.nsmallest(n,
self.individuals,
key=lambda indv: indv.score)
def worst_individuals(self, n, gender=None):
if gender is not None:
return heapq.nlargest(n,
self.segregated(gender),
key=lambda indv: indv.score)
return heapq.nlargest(n,
self.individuals,
key=lambda indv: indv.score)
class Individual():
def __init__(self, first=None, other=None, gender=None):
if first is None and other is None:
self.currentDNA = [random.choice(all_chars) for i in target]
self.genes = self.DNA2Genes()
elif first.gender != other.gender:
self.genes = []
for n, gene in enumerate(first.genes):
self.genes.append(random.choice([gene,
other.genes[n]]
))
self.currentDNA = [char for gene in self.genes for char in gene]
if random.randrange(0, 100) > 10:
self.currentDNA = self.mutate()
self.genes = self.DNA2Genes()
self.currentDNA = ''.join(self.currentDNA)
self.gender = gender or random.choice(['Male', 'Female'])
self.score = self.score_individual(target)
def score_individual(self, target_genes):
score = 0
for i, gene in enumerate(target_genes):
multiplier = ord(self.currentDNA[i]) - ord(gene)
score += multiplier * multiplier
return score
def DNA2Genes(self):
fourth = len(self.currentDNA) // 4
genes = [self.currentDNA[:fourth],
self.currentDNA[fourth:2 * fourth],
self.currentDNA[2 * fourth: - fourth],
self.currentDNA[- fourth:]
]
return genes
def mutate(self):
for count in range(random.randrange(len(self.currentDNA))):
chance = random.randrange(0, 7)
char = random.randrange(len(self.currentDNA))
if chance == 0:
pass
if char < (len(self.currentDNA) - 1) and char > 1:
if chance == 1:
self.currentDNA[char] = self.currentDNA[char + 1]
if chance == 2:
self.currentDNA[char] = self.currentDNA[char - 1]
if char < (len(self.currentDNA) - 2) and char > 2:
if chance == 3:
self.currentDNA[char] = self.currentDNA[char + 2]
if chance == 4:
self.currentDNA[char] = self.currentDNA[char - 2]
if chance == 5:
self.currentDNA[char] = chr(ord(self.currentDNA[char]) + 1)
if chance == 6:
self.currentDNA[char] = chr(ord(self.currentDNA[char]) - 1)
return self.currentDNA
gen_count = 0
best = 99999
lastprintgen = 0
initial_gen_size = random.randrange(100, 150) * 2
current_gen = Generation(size=initial_gen_size)
while target != current_gen.best_individuals(1)[0].currentDNA:
old_gen = current_gen
current_gen = Generation(prev=old_gen)
gen_count += 1
last = current_gen.best_individuals(1)[0].score
if last < best:
best = last
print("best score so far: %s,"
" population %s,"
" generations since last print: %s"
% (best,
len(current_gen.individuals),
gen_count - lastprintgen))
lastprintgen = gen_count
print("Final generation count: {}".format(gen_count),
"Target string: {}".format(target),
"Initial generation size: {}".format(initial_gen_size),
sep="\n")