taguo #6267(2008/05/06 18:03 GMT) [ C# ] Rating1/1=1.00
総当りでやってみました。 ただ、1手目から総当りで評価すると時間がかかりすぎるので1~3手目は決めうちしてます。 以下の評価で場所を決めます。 ・相手が勝てるパターンがない ・自分が相手よりも先に勝てる ・自分が勝てるパターンが多い 相手のリーチが2箇所になる場合の評価が別になってしまいましたが、統一的にできるのかもしれません。 結果 Aho Aho { result = Aho, count = 5873 } { result = Aho, count = 2914 } { result = draw, count = 1213 } 00:00:02.4866065 Aho Kasikoi { result = Kasikoi, count = 8790 } { result = draw, count = 1210 } 00:15:10.8550190 Kasikoi Aho { result = Kasikoi, count = 8927 } { result = draw, count = 1073 } 00:02:20.3465250 Kasikoi Kasikoi { result = draw, count = 10000 } 00:15:05.6982845
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
using System; using System.Collections.Generic; using System.Linq; using System.Drawing; using Strategy = System.Func<System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.Drawing.Point, bool>>, bool, System.Drawing.Point>; using Board = System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.Drawing.Point, bool>>; using System.Diagnostics; namespace DoukakuMarubatsu { public static class Marubatsu { internal static void Start() { var strategySet = new[] { new Strategy [] { Aho, Aho }, new Strategy [] { Aho, Kasikoi }, new Strategy [] { Kasikoi, Aho }, new Strategy [] { Kasikoi, Kasikoi }, }; foreach( var set in strategySet ) { Stopwatch sw = new Stopwatch(); sw.Start(); Debug.WriteLine( string.Format( "{0} {1}", set[ 0 ].Method.Name, set[ 1 ].Method.Name ) ); foreach( var result in from i in Enumerable.Range( 1, 10000 ) let result = Start( set[ 0 ], set[ 1 ] ) let dummy = Console.WriteLine( i ) is object group result by result into g let count = g.Count() orderby count descending select new { result = g.Key.HasValue ? ( g.Key.Value ? set[ 0 ].Method.Name : set[ 1 ].Method.Name ) : "draw", count } ) Debug.WriteLine( result ); Debug.WriteLine( sw.Elapsed ); } Console.ReadLine(); } static bool? Start( Strategy getNext1, Strategy getNext2 ) { var board = new Dictionary<Point, bool>(); return new[] { new { player = true, getNext = getNext1 }, new { player = false, getNext = getNext2 } } .Cycle() .Select( p => { board.Add( p.getNext( board, p.player ), p.player ); return GetWinner( board, p.player ); } ) .First( winner => winner.HasValue || board.Count == 9 ); } static bool? GetWinner( Board board ) { return GetWinner( board, true ) ?? GetWinner( board, false ); } static bool? GetWinner( Board board, bool player ) { Func<Func<int, Point>, bool> check = toPoint => new[] { 0, 1, 2 }.All( i => board.Any( p => p.Key == toPoint( i ) && p.Value == player ) ); if( new[] { 0, 1, 2 }.Any( i => check( x => new Point( x, i ) ) || check( y => new Point( i, y ) ) ) || check( i => new Point( i, i ) ) || check( i => new Point( 2 - i, i ) ) ) return player; else return null; } static IEnumerable<Point> EnumeratePoints() { foreach( var y in new[] { 0, 1, 2 } ) foreach( var x in new[] { 0, 1, 2 } ) yield return new Point( x, y ); } static void TraceBoard( Board board ) { foreach( var p in EnumeratePoints() ) Console.Write( ToMark( board.Contains( p ) ? (bool?)board.First( pp => pp.Key == p ).Value : null ) + ( p.X == 2 ? "\n" : "" ) ); Console.WriteLine( "----" ); } static string ToMark( bool? player ) { return player.HasValue ? ( player.Value ? "○" : "×" ) : "□"; } static Random _rnd = new Random(); static Point Aho( Board board, bool player ) { var putable = board.EnumeratePutable().ToArray(); return putable[ _rnd.Next( putable.Length ) ]; } #region kasikoi static Point Kasikoi( Board board, bool player ) { {//本来は不要だが総当りの回数を減らすために決めうち if( board.Count() == 0 ) return new Point( 1, 1 ); if( board.Count() == 1 ) return !board.Contains( new Point( 1, 1 ) ) ? new Point( 1, 1 ) : new Point( 0, 0 ); if( board.Count() == 2 ) return !board.Contains( new Point( 1, 1 ) ) ? new Point( 1, 1 ) : new[] { 0, 2 }.SelectMany( y => new[] { 0, 2 }.Select( x => new Point( x, y ) ) ).First( kado => !board.Contains( kado ) ); } //b:boardPattern d:dictionary p:player/keyValuePair/point o:otherPlayer g:group c:candidate var candidates0 = ( from b in EnumeratePatterns( board, Enumerable.Empty<KeyValuePair<Point, bool>>(), player ) let d = board.Concat( b ).ToDictionary( p => p.Key, p => p.Value ) let winner = GetWinner( d ) let count = d.Count group new { winner, count } by b.First().Key into g let pMin = g.Min( c => c.winner == player ? c.count : 9 ) let oMin = g.Min( c => c.winner == !player ? c.count : 9 ) let pCnt = g.Count( c => c.winner == player ) let oCnt = g.Count( c => c.winner == !player ) orderby pCnt + ( oCnt == 0 ? 1000 : 0 ) descending select new { point = g.Key, pMin, oMin } ).ToArray(); var katimenasi = candidates0.All( c => c.pMin > c.oMin ); var candidates = candidates0.Where( c => ( katimenasi || c.pMin <= c.oMin ) && CanPut( board, c.point, player ) ); return candidates.Count() > 0 ? candidates.First().point : board.EnumeratePutable().First(); } static bool CanPut( Board board, Point point, bool player ) { var virtualBoard = board.Add( point, player ); return virtualBoard.EnumeratePutable().All( otherPlayerPoint => { var virtualBoard2 = virtualBoard.Add( otherPlayerPoint, !player ); //相手が次の次で勝てるポイント一覧 var list = virtualBoard2.EnumeratePutable() .Where( otherPlayerPoint2 => GetWinner( virtualBoard2.Add( otherPlayerPoint2, !player ), !player ) == !player ); //相手が勝てるポイントが複数あってもそこに自分が置いて勝てるなら問題なし return list.Count() < 2 || list.Any( p => GetWinner( virtualBoard2.Add( p, player ), player ) == player ); } ); } static IEnumerable<Board> EnumeratePatterns( Board board, Board points, bool player ) { var putable = board.EnumeratePutable().Except( points.Select( pp => pp.Key ) ); if( putable.Count() == 0 || GetWinner( board ).HasValue ) yield return points; else foreach( var p in putable ) foreach( var points2 in EnumeratePatterns( board.Add( p, player ), points.Add( p, player ), !player ) ) yield return points2; } #endregion #region extension public static IEnumerable<Point> EnumeratePutable( this Board board ) { return EnumeratePoints().Except( board.Select( pp => pp.Key ) ); } public static Board Add( this Board board, Point p, bool player ) { return board.Concat( Enumerable.Repeat( new KeyValuePair<Point, bool>( p, player ), 1 ) ); } public static bool Contains( this Board board, Point point ) { return board.Any( p => p.Key == point ); } public static IEnumerable<T> Cycle<T>( this IEnumerable<T> src ) { while( true ) foreach( T t in src ) yield return t; } #endregion } }
Rating1/1=1.00-0+
1 reply [ reply ]
taguo #6318(2008/05/22 10:11 GMT) Rating0/0=0.00
評価方法を以下のようにすると先行の勝率が98%台に上がりました。 orderby pMin, pCnt descending, oCnt, oMin descending
[ reply ]
taguo #6267() [ C# ] Rating1/1=1.00
総当りでやってみました。 ただ、1手目から総当りで評価すると時間がかかりすぎるので1~3手目は決めうちしてます。 以下の評価で場所を決めます。 ・相手が勝てるパターンがない ・自分が相手よりも先に勝てる ・自分が勝てるパターンが多い 相手のリーチが2箇所になる場合の評価が別になってしまいましたが、統一的にできるのかもしれません。 結果 Aho Aho { result = Aho, count = 5873 } { result = Aho, count = 2914 } { result = draw, count = 1213 } 00:00:02.4866065 Aho Kasikoi { result = Kasikoi, count = 8790 } { result = draw, count = 1210 } 00:15:10.8550190 Kasikoi Aho { result = Kasikoi, count = 8927 } { result = draw, count = 1073 } 00:02:20.3465250 Kasikoi Kasikoi { result = draw, count = 10000 } 00:15:05.6982845Rating1/1=1.00-0+
1 reply [ reply ]