<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Le blog de Quentin</title>
	<atom:link href="https://blog.developpez.com/quentin-cormier/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/quentin-cormier</link>
	<description></description>
	<lastBuildDate>Sat, 08 May 2010 11:37:32 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Epic-chess, ou la réalisation d&#8217;un moteur d&#8217;echecs en ocaml</title>
		<link>https://blog.developpez.com/quentin-cormier/p8796/ia/epic_chess_moteur_d_echecs_en_ocaml</link>
		<comments>https://blog.developpez.com/quentin-cormier/p8796/ia/epic_chess_moteur_d_echecs_en_ocaml#comments</comments>
		<pubDate>Thu, 29 Apr 2010 12:30:06 +0000</pubDate>
		<dc:creator><![CDATA[quentin_]]></dc:creator>
				<category><![CDATA[IA]]></category>
		<category><![CDATA[ocaml]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour, Il y a quelques semaines, j&#8217;ai découvert les échecs (quelques astuces de base du moins), et j&#8217;ai vite été tenté d&#8217;essayer de faire un programme informatique qui pourrait calculer dans une position donnée le meilleur coup à jouer, et donc jouer aux échecs. Ce qui au départ devait être le projet d&#8217;une soirée ou deux c&#8217;est vite retrouvé un projet plus important. Dans ce billet, je vais essayer de vous décrire les étapes et [&#8230;]]]></description>
				<content:encoded><![CDATA[<p><center><a href="http://caml.inria.fr/" target="_blank"><img src="http://damien-guichard.developpez.com/logos/logo_caml.png"/></a></center></p>
<p>Bonjour, </p>
<p>Il y a quelques semaines, j&rsquo;ai découvert les échecs (quelques astuces de base du moins), et j&rsquo;ai vite été tenté d&rsquo;essayer de faire un programme informatique qui pourrait calculer dans une position donnée le meilleur coup à jouer, et donc jouer aux échecs. Ce qui au départ devait être le projet d&rsquo;une soirée ou deux c&rsquo;est vite retrouvé un projet plus important. Dans ce billet, je vais essayer de vous décrire les étapes et les choix que j&rsquo;ai réalisé dans la création de ce moteur d&rsquo;échec.</p>
<p><img src="http://blog.developpez.com/media/screenshot.png" width="968" height="853" alt="Sreenshot" /></p>
<p><span id="more-1"></span><br />
<strong>Fonctionnement d&rsquo;un programme de jeux d&rsquo;échecs.</strong></p>
<p>L&rsquo;idée principale qui se cache derrière tous les moteurs d&rsquo;échecs, c&rsquo;est de jouer virtuellement tous les coups possibles et de prendre celui qui nous semble le meilleur.<br />
Par exemple, si vous avez 3 coups A, B et C, alors on commence par jouer le coup A, et on joue ensuite pour l&rsquo;adversaire. Si l&rsquo;adversaire peut jouer les coups A&rsquo; et B&rsquo;, alors on lui fait jouer le coup A&rsquo;, puis on regarde les coups que l&rsquo;on peut jouer, etc., et ceci jusqu&rsquo;à une profondeur donnée. Le but va être de maximiser notre score final en choisissant, dans notre exemple, le coup A, B, ou C qui semble être le meilleur. On joue pour l&rsquo;adversaire de la même façon que l&rsquo;on jouerait pour nous-même : on le fait maximiser son score.</p>
<p>C&rsquo;est l&rsquo;algorithme min-max, qui consiste à maximiser nos gains en supposant que l&rsquo;adversaire va minimiser ses pertes.</p>
<p>Par exemple, prenons l&rsquo;arbre des coups de la partie suivante.<br />
<img src="http://upload.wikimedia.org/wikipedia/commons/7/72/Min_Max_Sample_1.png" title="" /><br />
Nous sommes à la position A, et nous pouvons jouer les coups B1, B2 ou B3.<br />
Si on joue B1, soit l&rsquo;adversaire joue C1 et perd 12 points, soit il joue C2 et perd 10 points, soit il joue C3 et perd 3 points.</p>
<p>On considère donc que l&rsquo;adversaire va jouer C3 et on évalue donc le coup B1 à 3 points.<br />
Si on joue B2, la branche est alors évaluée à 5 points (l&rsquo;adversaire joue C4).<br />
Si on joue B3, la branche est évaluée à 2 points (l&rsquo;adversaire joue C8).</p>
<p>Au final, le meilleur coup se révèle être B2, puisqu&rsquo;il permet d&rsquo;obtenir au minimum 5 points.</p>
<p>Voilà pour le principe de base. Il existe par la suite quelques optimisations qui permettent par exemple de ne pas explorer tous les cas.</p>
<p>Pour réaliser cet algorithme nous avons donc besoin d&rsquo;une fonction qui donne à un instant donné tous les coups que l&rsquo;on peut jouer selon les règles des échecs, une fonction pour jouer et pour annuler un coup, et une fonction pour évaluer une position.</p>
<p><strong>Les règles des échecs</strong></p>
<p>La première étape consiste donc à représenter le plateau des échecs. J&rsquo;ai utilisé pour cela un tableau en deux dimensions, bien qu&rsquo;il existe des solutions plus performantes (comme les bitboard).<br />
J&rsquo;utilise une classe Board qui a été réalisée par un ami, cgizmo. C&rsquo;est une classe générique à tous les jeux de plateaux rectangulaires, qui contient toutes les fonctions pour manipuler un tableau à deux dimensions, mais aussi une fonction pour afficher un plateau, et une fonction pour annuler un coup (rollback).</p>
<p>Quand je veux jouer un coup, je fais plateau#start_move(); je manipule mon plateau, puis je termine le coup avec plateau#end_move().</p>
<p>Ainsi, pour annuler le coup, je fais simplement plateau#rollback() et la classe annule toutes les modifications de mon plateau qui ont été faites entre les deux marqueurs.</p>
<p>J&rsquo;ai ensuite créé une classe Chess, qui utilise la classe Board, mais qui est spécifique aux échecs.<br />
Elle est capable de savoir si un coup est valide, de retourner tous les coups valides, de savoir si on est échec et mat, pat, etc.</p>
<p>Cette classe est assez importante en taille : les règles des échecs sont en fait assez compliqués.<br />
Par exemple :<br />
-Les pions avance de une case normalement, sauf s&rsquo;ils peuvent manger une pièce adverse (ils peuvent se déplacer alors sur les côtés) ou s&rsquo;ils sont sur leur première ligne (ils peuvent avancer de deux), ou encore même s&rsquo;ils se trouvent sur la 5ième ligne et que l&rsquo;adversaire avance un pion de deux cases (prise en passant).<br />
-On a le droit de roquer une fois par partie s&rsquo;il n&rsquo;y a aucune pièce entre le roi et la tour, et si aucune des cases entre le roi et la tour n&rsquo;est pas menacée, etc.</p>
<p>Bref, je n&rsquo;ai pas implémenté toutes les règles d&rsquo;un coup, j&rsquo;ai fait cela par étapes.<br />
Pour tester que tout allait bien, j&rsquo;ai simplement fait jouer les deux joueurs en prenant à chaque tour un coup au hasard parmi les coups valides. La partie doit finir soit par un pat, soit par un mat !</p>
<p>Il y a également dans cette classe la fonction d&rsquo;évaluation qui accorde un score à une position d&rsquo;une partie. Par exemple, elle peut compter les pièces restantes sur le plateau, regarder la position des pièces (les pièces aux centres sont valorisés par ex), etc.<br />
La mienne est très simple, et c&rsquo;est un point qu&rsquo;il faudrait améliorer car la fonction d&rsquo;évaluation est très importante dans un moteur d&rsquo;échec.</p>
<p>Pour finir, voici la signature de ma classe Chess :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">chess : <br />
&nbsp; object <br />
&nbsp; &nbsp; val mutable board : field board <br />
&nbsp; &nbsp; val mutable castling_b : bool * int <br />
&nbsp; &nbsp; val mutable castling_w : bool * int <br />
&nbsp; &nbsp; val mutable king_b : int * int <br />
&nbsp; &nbsp; val mutable king_w : int * int <br />
&nbsp; &nbsp; val mutable moves : dep list <br />
&nbsp; &nbsp; val mutable piece_captured : bool <br />
&nbsp; &nbsp; val mutable turn : color <br />
&nbsp; &nbsp; method private add_move : dep -&gt; unit <br />
&nbsp; &nbsp; method apply : field array array -&gt; unit <br />
&nbsp; &nbsp; method private available_move : <br />
&nbsp; &nbsp; &nbsp; int * int -&gt; int * int -&gt; piece_type -&gt; bool -&gt; bool * dep option <br />
&nbsp; &nbsp; method board : field board <br />
&nbsp; &nbsp; method private can_castling : bool <br />
&nbsp; &nbsp; method cancel : unit <br />
&nbsp; &nbsp; method capture : bool <br />
&nbsp; &nbsp; method castling : color -&gt; bool * int <br />
&nbsp; &nbsp; method private check_castling : int * int -&gt; int * int -&gt; bool <br />
&nbsp; &nbsp; method private check_enpassant : int * int -&gt; int * int -&gt; bool <br />
&nbsp; &nbsp; method check_move : <br />
&nbsp; &nbsp; &nbsp; int * int -&gt; int * int -&gt; piece_type -&gt; bool -&gt; bool * dep option <br />
&nbsp; &nbsp; method edit_castling : color -&gt; bool -&gt; bool -&gt; unit <br />
&nbsp; &nbsp; method edit_king : color -&gt; int * int -&gt; unit <br />
&nbsp; &nbsp; method edit_turn : unit <br />
&nbsp; &nbsp; method eval : color -&gt; ExtendN.score <br />
&nbsp; &nbsp; method private get_all : bool -&gt; bool -&gt; dep list <br />
&nbsp; &nbsp; method get_moves : bool -&gt; dep list <br />
&nbsp; &nbsp; method init : unit <br />
&nbsp; &nbsp; method is_check : int * int -&gt; bool <br />
&nbsp; &nbsp; method king : color -&gt; int * int <br />
&nbsp; &nbsp; method private mouvements_p : <br />
&nbsp; &nbsp; &nbsp; int * int -&gt; bool -&gt; ((int * int) * piece_type) list <br />
&nbsp; &nbsp; method move_piece : dep -&gt; unit <br />
&nbsp; &nbsp; method moves : dep list <br />
&nbsp; &nbsp; method print : unit <br />
&nbsp; &nbsp; method private printc : int * int -&gt; unit <br />
&nbsp; &nbsp; method turn : color <br />
&nbsp; end</div></div>
<p><strong>L&rsquo;intelligence artificielle</strong></p>
<p>J&rsquo;ai donc commencé par écrire l&rsquo;algorithme min-max, qui m&rsquo;a permis de voir 3 demis-coups à l&rsquo;avance, ce qui est assez ridicule : mais quelle joie de voir mon truc tenter une variante du coup de berger :p !</p>
<p>Je me suis tourné ensuite vers les coupures alpha/bêta. C&rsquo;est une amélioration de min/max qui repose sur cette constatation : supposons que vous pouvez jouer 3 coups, et que le premier coup vous rapporte 3 points. Vous explorez alors le deuxième coup et vous voyez que l&rsquo;adversaire peut jouer un coup qui va ne vous rapporter que 2 points. Comme on sait que vous pouvez gagner 3 points en jouant le premier coup, cela ne sert à rien de continuer sur le deuxième coup, il sera forcément moins bon que le premier ! On peut donc couper cette brancher et explorer directement le troisième coup.</p>
<p>Voici une implémentation de cet algorithme :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">let rec alphabeta game alpha beta ck prof = &nbsp;<br />
&nbsp; let rec loop best al bt = function &nbsp;<br />
&nbsp; &nbsp; | [] -&gt; &nbsp; <br />
&nbsp; let sb, cb = best in &nbsp;<br />
&nbsp; &nbsp; (* Si on a perdu dans tous les cas *) &nbsp;<br />
&nbsp; &nbsp; if sb = MInf then &nbsp; <br />
&nbsp; &nbsp; &nbsp; let lm = game#get_moves true in &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; if lm &lt;&gt; [] then (MInf, List.hd lm) &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; else &nbsp;<br />
&nbsp; &nbsp; if game#is_check (game#king (game#turn)) then best &nbsp;<br />
&nbsp; &nbsp; else &nbsp;<br />
&nbsp; &nbsp; &nbsp; (* Si il y a pat *) &nbsp;<br />
&nbsp; &nbsp; &nbsp; (N 0, cb) &nbsp;<br />
&nbsp; &nbsp; else &nbsp;<br />
&nbsp; &nbsp; &nbsp; best &nbsp;<br />
&nbsp; &nbsp; | (s, mvt)::tail -&gt; &nbsp;<br />
&nbsp; game#move_piece mvt; &nbsp;<br />
&nbsp; let score = &nbsp; <br />
&nbsp; &nbsp; if prof = 0 || s = MInf || s = PInf then s &nbsp;<br />
&nbsp; &nbsp; else let s, _ = alphabeta game ((--) bt) ((--) al) false (prof-1) in (--) s &nbsp;<br />
&nbsp; in &nbsp;<br />
&nbsp; &nbsp; game#cancel; &nbsp;<br />
&nbsp; &nbsp; let n_best = if score &gt;&gt;= (fst best) then (score, mvt) else best in &nbsp;<br />
&nbsp; &nbsp; let n_alpha = if fst n_best &gt;&gt;= al then fst n_best else al in &nbsp;<br />
&nbsp; &nbsp; &nbsp; if n_alpha &gt;&gt; bt then n_best &nbsp;<br />
&nbsp; &nbsp; &nbsp; else &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; loop n_best n_alpha bt tail &nbsp;<br />
&nbsp; in &nbsp;<br />
&nbsp; &nbsp; (* On récupère et on trie les coups possibles *) &nbsp;<br />
&nbsp; let l = game#get_moves ck in &nbsp;<br />
&nbsp; let nl = List.map (fun mvt -&gt; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;game#move_piece mvt; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;let s = game#eval !!(game#turn) in game#cancel; (s, mvt) &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; ) l in &nbsp;<br />
&nbsp; let l' = List.sort (fun (a, _) (b, _) -&gt; compare b a) nl in &nbsp;<br />
&nbsp; &nbsp; loop (MInf, Dep((0,0), (0,0))) alpha beta l'</div></div>
<p>Pour plus d&rsquo;information sur cet algorithme, je vous recommande cette page qui est assez bien faite : </p>
<p>http://jeudechecs.ifrance.com/pageechecs.htm</p>
<p>J&rsquo;arrivais à voir grâce à cet algorithme jusqu&rsquo;à 5 demi-coups à l&rsquo;avance. Je commençais à battre un bon débutant :).</p>
<p>J&rsquo;ai essayé pleins de modifications de cet algo. J&rsquo;ai essayé de faire une version qui au lieu de me renvoyer le meilleur coup présumé, me renvoi l&rsquo;arbre entier des coups, ainsi qu&rsquo;une fonction prolonge_coup qui pourrait me prolonger l&rsquo;arbre jusqu&rsquo;à une profondeur donnée.<br />
Ainsi par exemple, je pourrais créer l&rsquo;arbre sur une profondeur de 4 demi-coups, sélectionner les 5 meilleurs coups à cette profondeur, puis continuer avec ces 5 coups jusqu&rsquo;à 6 demis-coups par exemple.</p>
<p>La difficulté dans la fonction prolonge_coups est de bien faire remonter les valeurs de alpha et de bêta dans l&rsquo;arbre pour produire toutes les coupures de la même façon que l&rsquo;algorithme alpha/bêta.</p>
<p>Voici l&rsquo;implémentation de tout cela.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">type tree = &nbsp;<br />
&nbsp; | B of tree list <br />
&nbsp; | Node of feuille * tree list <br />
&nbsp; | Leaf of feuille <br />
&nbsp; | End of score <br />
;; <br />
&nbsp;<br />
[...] <br />
&nbsp;<br />
let rec alphabeta game alpha beta prof = &nbsp;<br />
&nbsp; let rec loop best al bt l = function &nbsp;<br />
&nbsp; &nbsp; | [] -&gt; &nbsp;<br />
&nbsp; if best = MInf then &nbsp;<br />
&nbsp; &nbsp; let lm = game#get_moves true in &nbsp; <br />
&nbsp; &nbsp; &nbsp; if lm &lt;&gt; [] then ([Leaf(MInf, (List.hd lm))], MInf) &nbsp;<br />
&nbsp; &nbsp; &nbsp; else &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; if game#is_check (game#king (game#turn)) then ([End MInf], MInf) &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; else &nbsp;<br />
&nbsp; &nbsp; (* Si il y a pat *) &nbsp;<br />
&nbsp; &nbsp; ([End (N 0)], N 0) &nbsp;<br />
&nbsp; else &nbsp;<br />
&nbsp; &nbsp; (l, best) &nbsp;<br />
&nbsp; &nbsp; | (s, mvt)::tail -&gt; &nbsp;<br />
&nbsp; game#move_piece mvt; &nbsp; <br />
&nbsp; let tree, s = &nbsp; <br />
&nbsp; &nbsp; if prof &lt;= 0 || s = PInf then (Leaf(s, mvt), s) &nbsp;<br />
&nbsp; &nbsp; else &nbsp; <br />
&nbsp; &nbsp; &nbsp; let r, s = alphabeta game ((--) bt) ((--) al) (prof -1) in &nbsp;<br />
&nbsp; &nbsp; &nbsp; let s = (--) s in &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; (Node ((s, mvt), r), s) &nbsp;<br />
&nbsp; in &nbsp;<br />
&nbsp; &nbsp; game#cancel; &nbsp;<br />
&nbsp; &nbsp; if s = PInf then ([tree], PInf) &nbsp;<br />
&nbsp; &nbsp; else &nbsp;<br />
&nbsp; &nbsp; &nbsp; let n_best = if s &gt;&gt; best then s else best in &nbsp;<br />
&nbsp; &nbsp; &nbsp; let n_alpha = if n_best &gt;&gt;= al then n_best else al in &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; (* &nbsp; <br />
&nbsp; &nbsp; &nbsp; Si il y a une coupure, on renvoit le coup qui a produit la coupure, &nbsp; <br />
&nbsp; &nbsp; &nbsp; suivit des autres que l'on a déjà calculé ou que l'on ne calcule pas. &nbsp;<br />
&nbsp; &nbsp; &nbsp; Il seront necessaire pour prolonger l'arbre &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; *) &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; if n_alpha &gt;&gt; bt then (tree::l@(l_to_tree tail), n_best) &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; else loop n_best n_alpha bt (if s &gt;&gt; MInf then insert tree l else l) tail &nbsp;<br />
&nbsp; in &nbsp;<br />
&nbsp; let l = game#get_moves false in &nbsp;<br />
&nbsp; let nl= List.map (fun mvt -&gt; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; game#move_piece mvt; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let s = game#eval !!(game#turn) in game#cancel; (s, mvt) &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp;) l in &nbsp;<br />
&nbsp; let l' = List.sort (fun (a, _) (b, _) -&gt; compare b a) nl in &nbsp;<br />
&nbsp; let r, s = (loop MInf alpha beta [] l') in &nbsp;<br />
&nbsp; &nbsp; (r, s) &nbsp;<br />
;; &nbsp;<br />
[..] <br />
let rec prolonge_tree game alpha beta n tree = &nbsp; <br />
&nbsp; let rec loop best al bt l = function &nbsp;<br />
&nbsp; &nbsp; | [] -&gt; &nbsp; <br />
&nbsp; (l, best) &nbsp;<br />
&nbsp; &nbsp; | t::tail -&gt; &nbsp;<br />
&nbsp; let nt = prolonge_tree game ((--)bt) ((--)al) (n-1) t in &nbsp;<br />
&nbsp; let s = get_score_of_tree nt in &nbsp;<br />
&nbsp; let n_best = if s &gt;&gt;= best then s else best in &nbsp;<br />
&nbsp; let n_alpha = if n_best &gt;&gt;= al then n_best else al in &nbsp;<br />
&nbsp; &nbsp; if n_alpha &gt;&gt; bt then (nt::l@tail, n_best) &nbsp;<br />
&nbsp; &nbsp; else loop (if s &gt;&gt;= best then s else best) n_alpha bt (insert nt l) tail &nbsp;<br />
&nbsp; in &nbsp;<br />
&nbsp; &nbsp; match tree with &nbsp;<br />
&nbsp; &nbsp; &nbsp; | Node((s, d), l) -&gt; &nbsp;<br />
&nbsp; &nbsp; game#move_piece d; &nbsp;<br />
&nbsp; &nbsp; let r, score = loop MInf alpha beta [] l in &nbsp;<br />
&nbsp; &nbsp; &nbsp; game#cancel; &nbsp;<br />
&nbsp; &nbsp; &nbsp; Node(((--)score, d), r) &nbsp;<br />
&nbsp; &nbsp; &nbsp; | Leaf(s, d) -&gt; &nbsp;<br />
&nbsp; &nbsp; game#move_piece d; &nbsp;<br />
&nbsp; &nbsp; let r, s = alphabeta game alpha beta n in &nbsp;<br />
&nbsp; &nbsp; &nbsp; game#cancel; &nbsp;<br />
&nbsp; &nbsp; &nbsp; Node(((--) s, d), r) &nbsp;<br />
&nbsp; &nbsp; &nbsp; | End score -&gt; End score &nbsp;<br />
&nbsp; &nbsp; &nbsp; | B l -&gt; &nbsp;<br />
&nbsp; &nbsp; let nl, _ = loop MInf alpha beta [] l in &nbsp;<br />
&nbsp; &nbsp; &nbsp; B nl &nbsp;<br />
;;</div></div>
<p>Cette version me permet donc de faire une sélection des coups, ce qui améliore un peu la performance du moteur (mais rien d&rsquo;extraordinaire).</p>
<p><strong>Quelques fioritures</strong></p>
<p>J&rsquo;ai rajouté un dictionnaire d&rsquo;ouvertures (j&rsquo;utilise les ouvertures de gnuchess) pour être meilleur en début de partie, ainsi qu&rsquo;un module xboard pour jouer avec l&rsquo;interface graphique xboard.</p>
<p><strong>Conclusion</strong></p>
<p>Bien que les performances de mon moteur soient plutôt médiocre, je garde un bon souvenir de ce projet. C&rsquo;était la première fois que j&rsquo;essayais de faire une intelligence artificielle. Les échecs ne sont pas franchement un très bon choix pour un premier jet, mais finalement je m&rsquo;en suis pas trop mal tiré.</p>
<p>Le code source, pas très intéressant, et dont l&rsquo;organisation en classes est franchement discutable est disponible ici : </p>
<p>http://code.google.com/p/epicchess/</p>
<p>J&rsquo;espère que ce retour sur ce petit projet vous a plu,<br />
Merci d&rsquo;avance pour tout vos commentaires, améliorations, questions, etc. !</p>
<p>Cordialement, </p>
<p>Quentin</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
