1 /**
2 *   Copyright: © 2012-2014 Anton Gushcha
3 *   License: Subject to the terms of the MIT license, as written in the included LICENSE file.
4 *   Authors:  NCrashed <ncrashed@gmail.com>,
5 *             LeMarwin <lemarwin42@gmail.com>,
6 *             Nazgull09 <nazgull90@gmail.com>
7 */
8 module devol.population;
9 
10 import std.stdio;
11 
12 import std.random;
13 import std..string;
14 import std.array;
15 import std.algorithm;
16 import std.conv;
17 import std.file;
18 import std.path;
19 import devol.serializable;
20 import dyaml.all;    
21 
22 public
23 {
24 	import devol.individ;
25 }
26 
27 interface PopAbstract : ISerializable
28 {
29 	void genName(size_t size);
30 	@property string name();
31 	@property void name(string val);
32 	@property size_t generation();
33 	@property void generation(size_t val);
34 	@property size_t length();
35 	IndAbstract opIndex( size_t i );
36 	IndAbstract[] opSlice( size_t a, size_t b );
37 	size_t opDollar();
38 	int opApply(int delegate(IndAbstract) dg);
39 	int opApply(int delegate(size_t, IndAbstract) dg);
40 	Node saveYaml();
41 }
42 
43 static string getDefChars()
44 {
45 	string ret = "qwertyuiopasdfghjklzxcvbnm";
46 	return ret ~ toUpper(ret);
47 }
48 
49 alias Population!(getDefChars, Individ) StdPop;
50 
51 class Population(alias nameChecker, IndType)
52 	if ( is(typeof(nameChecker()) == string) )
53 	: PopAbstract
54 {
55 	enum DefNameLength = 10;
56 	enum DefNameChars = getDefChars();
57 	
58 	alias Population!(nameChecker, IndType) thistype;
59 	alias IndType IndividType;
60 	
61 	@property size_t generation()
62 	{
63 		return iGeneration;
64 	}
65 	
66 	@property void generation(size_t val)
67 	{
68 		//if (val < iGeneration) return;
69 		iGeneration = val;
70 	}
71 	
72 	this()
73 	{
74 		inds = new IndType[0];
75 	}
76 	
77 	this(size_t size)
78 	{
79 		inds = new IndType[size];
80 		foreach( ref ind; inds)
81 		{
82 			ind = new IndType;
83 		}
84 	}
85 	
86 	void genName(size_t size = DefNameLength)
87 	{
88 		auto buff = new char[size];
89 		string chars = nameChecker();
90 		foreach(ref c; buff)
91 		{
92 			c = chars[uniform(0,chars.length)];
93 		}
94 		mName = buff.idup;
95 	}
96 	
97 	@property string name()
98 	{
99 		return mName;
100 	}
101 	 
102 	@property void name(string val)
103 	{
104 		string chars = nameChecker();
105 		foreach(i,c; val)
106 		{
107 			if ( chars.find(c).empty )
108 			{
109 				val = val[0..i] ~ chars[uniform(0,chars.length)] ~ val[i+1..$];
110 			}
111 		}
112 		mName = val;
113 	}
114 	
115 	@property size_t length()
116 	{
117 		return inds.length;
118 	}
119 	
120 	IndType opIndex( size_t i )
121 	{
122 		return cast(IndType)inds[i];
123 	}
124 	
125 	IndAbstract[] opSlice( size_t a, size_t b )
126 	{
127 		return cast(IndAbstract[])(inds[a..b]);
128 	}
129 	
130 	size_t opDollar()
131 	{
132 		return inds.length;
133 	}
134 	
135 	int opApply(int delegate(IndAbstract) dg)
136 	{
137 		int result = 0;
138 		
139 		foreach(i, ref ind; inds)
140 		{
141 			IndAbstract inda = (ind);
142 			result = dg(inda);
143 			if (result) break;
144 		}
145 		return result;
146 	}
147 	
148     int opApply(int delegate(size_t, IndAbstract) dg)
149     {
150         int result = 0;
151         
152         foreach(i, ref ind; inds)
153         {
154             IndAbstract inda = (ind);
155             result = dg(i, inda);
156             if (result) break;
157         }
158         return result;
159     }
160     
161 	auto opBinary(string m)(IndType val)
162 		if (m == "~")
163 	{
164 		inds ~= val;
165 		return this;
166 	}
167 	
168 	void addIndivid(IndType val)
169 	{
170 		if (val is null) return;
171 		inds ~= val;
172 	}
173 	
174 	void saveBests(string filename)
175 	{
176 		if (inds.length == 0) return;
177 
178 		auto sortedInds = inds.sort!"a.fitness > b.fitness";
179 		
180 		Individ best;
181 		int k = 0;
182 		
183 		std.stdio.File* f;
184 		try
185 		{
186             mkdirRecurse(filename);
187                 
188 			f = new std.stdio.File(filename~mName~"_g"~to!string(iGeneration), "w");
189 		
190 			do
191 			{
192 				best = sortedInds[k++];
193 				f.writeln("Individ №", k,":");
194 				f.writeln(best.programString());
195 				f.writeln("==================================");
196 			} while( k < sortedInds.length && sortedInds[k].fitness >= best.fitness);
197 		} catch(Exception e)
198 		{
199 			writeln("FAILED TO CREATE FILE TO WRITE RESULTED INDIVIDS!!");
200 		}
201 	}
202 	
203 	void saveAll(string filename)
204 	{
205 		std.stdio.File* f;
206 		try
207 		{
208 		    mkdirRecurse(filename);
209 		        
210 			f = new std.stdio.File(filename~mName~"_g"~to!string(iGeneration), "w");
211 			
212 			foreach(i,ind;inds)
213 			{
214 				f.writeln("Individ №", i,":");
215 				f.writeln(ind.programString());
216 				f.writeln("==================================");			
217 			}	
218 		} catch(Exception e)
219 		{
220 			writeln("FAILED TO CREATE FILE TO WRITE RESULTED INDIVIDS!!");
221 		}
222 	}
223 	
224 	@property auto dup()
225 	{
226 		auto ret = new Population!(nameChecker, IndType);
227 		ret.iGeneration = iGeneration;
228 		foreach(ind;inds)
229 			ret.inds ~= ind.dup;
230 		ret.mName = mName;
231 		return ret;
232 	}
233 	
234 	void clear()
235 	{
236 		inds.clear();
237 	}
238 	
239 	void saveBinary(OutputStream stream)
240 	{
241 	    stream.write(mName);
242 	    stream.write(iGeneration);
243 	    
244 	    stream.write(cast(ulong)inds.length);
245 	    foreach(ind; inds)
246 	    {
247 	        ind.saveBinary(stream);
248 	    }
249 	}
250 	
251 	Node saveYaml()
252 	{
253 	    auto builder = appender!(Node[]);
254 	    foreach(ind; inds)
255 	    {
256 	        builder.put(ind.saveYaml);
257 	    }
258 	    
259 	    return Node([
260 	        "name": Node(mName),
261 	        "generation": Node(iGeneration),
262 	        "individs": Node(builder.data)
263 	        ]);
264 	}
265 	
266 	static thistype loadBinary(InputStream stream)
267 	{
268 	    auto pop = new thistype();
269 	    char[] popName;
270 	    stream.read(popName);
271 	    pop.mName = popName.idup;
272 	    
273 	    stream.read(pop.iGeneration);
274 	    
275 	    ulong indsLength;
276 	    stream.read(indsLength);
277 	    auto builder = appender!(IndType[]);
278 	    foreach(i; 0..cast(size_t)indsLength)
279 	    {
280 	        auto ind = IndType.loadBinary(stream);
281 	        
282 	        if(ind is null)
283 	        {
284 	            throw new Exception("Loaded ind is null!");
285 	        }
286 	        
287 	        builder.put(new IndType(ind));
288 	    }
289 	    pop.inds = builder.data;
290 	    
291 	    return pop;
292 	}
293 	
294 	static thistype loadYaml(Node node)
295 	{
296 	    auto ret = new thistype();
297 	    
298 	    ret.mName = node["name"].as!string;
299 	    ret.iGeneration = node["generation"].as!size_t;
300 	    
301 	    auto builder = appender!(IndType[]);
302 	    foreach(Node subnode; node["individs"])
303 	    {
304 	        builder.put(IndType.loadYaml(subnode));
305 	    }
306 	    
307 	    ret.inds = builder.data;
308 	    
309 	    return ret;
310 	}
311 	
312     protected
313     {
314     	size_t iGeneration;
315     	IndType[] inds;
316     	string mName = "";
317     }
318 }