I have been trying to implement an AI for the computer using minimax with alpha-beta pruning, but I m facing an unidentifiable bug. The algorithm should calculate all the possible moves of its own and the other player too, but it isn't playing back the way it should.
Here is my minimax code :
public int minimax(int[] board, char symbol, int alpha, int beta, int depth = 2)
{
int win = util.checkwin(board);
int nsymbol = (symbol == 'X' ? 1 : 2);
int mult = (symbol == compside ? 1 : -1);
if (win != -1)
{
if (win == nsymbol)
return mult;
else if (win != 0)
return (mult * -1);
else
return 0;
}
if (depth == 0)
return 0;
int[] newboard = new int[9];
Array.Copy(board, newboard, 9);
int score, i, pos = -1;
ArrayList emptyboard = new ArrayList();
emptyboard = util.filterboard(newboard);
for (i = 0; i < emptyboard.Count; i++)
{
if (i > 0)
newboard[(int)emptyboard[i - 1]] = 0;
newboard[(int)emptyboard[i]] = nsymbol;
score = minimax(newboard, util.changeside(symbol), alpha, beta, depth - 1);
if (mult == 1)
{
if (score > alpha)
{
alpha = score;
pos = (int)emptyboard[i];
}
if (alpha >= beta)
break;
}
else
{
if (score < beta)
beta = score;
if (alpha >= beta)
break;
}
}
if (depth == origdepth)
return pos;
if (mult == 1)
return alpha;
else
return beta;
}
The details of undefined functions:
util.checkwin(int[] board) = checks the board for a possible won or drawn outboard or an incomplete board, and returns the winner as 1 or 2 (player X or O), 0 for a draw, and -1 for an incomplete board.
util.filterboard(int[] newboard) = returns an arraylist containing all the positions of empty locations in board given.
util.changeside(char symbol) = simply flips X to O and O to X and returns the result.
I have tried with the depth as 2 which means it will calculate the next 2 moves (if it is winning and if the opponent can win). But the results weren't what I expected. and it is also trying to play on a filled location occasionally.
Here is an output(depth = 2):
Turn: X
| |
1 | 2 | 3
__|___|__
| |
4 | 5 | 6
__|___|__
| |
7 | 8 | 9
| |
Enter Your Choice:
Turn: O
| |
1 | 2 | 3
__|___|__
| |
X | 5 | 6
__|___|__
| |
7 | 8 | 9
| |
Enter Your Choice: 5
Turn: X
| |
1 | 2 | 3
__|___|__
| |
X | O | 6
__|___|__
| |
7 | 8 | 9
| |
Enter Your Choice:
Turn: O
| |
1 | X | 3
__|___|__
| |
X | O | 6
__|___|__
| |
7 | 8 | 9
| |
Enter Your Choice: 1
Turn: X
| |
O | X | 3
__|___|__
| |
X | O | 6
__|___|__
| |
7 | 8 | 9
| |
Enter Your Choice:
Turn: O
| |
O | X | 3
__|___|__
| |
X | O | 6
__|___|__
| |
7 | X | 9
| |
Enter Your Choice: 9
| |
O | X | 3
__|___|__
| |
X | O | 6
__|___|__
| |
7 | X | O
| |
O Wins
But it still fails to recognize my winning move.
All the other functions have been tested when played user against a user and they are all working fine. I would appreciate some help.
I am happy to provide my full code, if necessary and anything else required.
A couple of observations.
1) The if (depth == 0) return 0; should be changed to something like
if (depth == 0) return EvaluatePosition();,
because currently your algorithm will return 0 (score, corresponding to a draw) whenever it reaches depth zero (while the actual position at zero depth might not be equal - for instance, one of the sides can have huge advantage). EvaluatePosition() function should reflect the current board position (it should say something like "X has an advantage", "O is losing", "The position is more or less equal" etc, represented as a number). Note, that this will matter only if depth == 0 condition is triggered, otherwise it is irrelevant.
2) Do you really need this emptyboard stuff? You can iterate over all squares of the newboard and once you find an empty square, copy the original board, make the move on this empty square and call minimax with the copied and updated board. In pseudocode it will look something like this:
for square in board.squares:
if square is empty:
board_copy = Copy(board)
board_copy.MakeMove(square)
score = minimax(board_copy, /*other arguments*/)
/*the rest of minimax function*/
3) The if (alpha >= beta) break; piece is present in both branches (for mult == 1 and mult != 1), so you can put it after the if-else block to reduce code repetition.
4) Check if your algorithm is correct without alpha-beta pruning. The outcomes of plain minimax and alpha-beta pruning minimax should be the same, but plain minimax is easier to understand, code and debug. After your plain minimax is working properly, add enhancements like alpha-beta pruning and others.
Related
I want to make my own little encryption (it not must be secure).
I thought it would be a good idea, if the entered password would be converted to binary and then I want to change all the 1 to (2 | 3 | 9 | 7) so if we had
1101 = 2907 | 9703 ... and so on
So if we would enter 9703 we could decrypt it to 1101 again.
But I can’t find a Method to Replace these.
The Replace() Method would only do this:
1101 -> 2202 | 1101 -> 9909
And yes, i know that is not a good Method to encrypt something but I just want to code a very simple encryption on my own.
int[] replace_Ones = { 2, 3, 9, 7 };
int ServerEncryption = 1101010000111001;
ServerEncryption.Replace(1, 2);
Starting from this:
int[] replace_Ones = { 2, 3, 9, 7 }; // I removed the 0 ;)
long ServerEncryption = 1101010000111001;
You can make a method that does the following:
long FunnyEncrypt( long pseudobinary )
{
long result = 0;
long scope = 1;
while( pseudobinary > 0 )
{
// place 2 or 3 or 7 or 9 0 or 1
result += scope * GetRandomReplacement() * ( pseudobinary % 10 );
scope *= 10;
pseudobinary = pseudobinary / 10; // decimal right shift 110 / 10 -> 11
}
return result;
}
Disclaimer: untested! Corrections welcome.
GetRandomReplacement is left for practice :D - but it basically is "pick a random int out of [0..3] and use it as index into the array of [2,3,7,9]".
Example:
example input: 1101
| iteration | input | result | scope | -> | input | result | scope | GetRandomRepl |
| 1 | 1101 | 0 | 1 | -> | 110 | 7 | 10 | 7 |
| 2 | 110 | 7 | 10 | -> | 11 | 7 | 100 | - |
| 3 | 11 | 7 | 100 | -> | 1 | 307 | 1000 | 3 |
| 4 | 1 | 307 | 1000 | -> | 0 | 2307 | 10000 | 2 |
=> Result = 2307
EDIT: changed to long after testing, did not see the number is too big for int: https://dotnetfiddle.net/5X4lZu
If the problem is to replace '1' value of a binary string to a random value in { 2, 3, 9, 7 } you could do like this:
char[] replace_Ones = { '2', '3', '9', '7' };
StringBuilder ServerEncryption =new StringBuilder("1101010000111001");
Random r = new Random();
for (int i=0 ; i < ServerEncryption.Length ; i++)
{
if (ServerEncryption[i] != '0')
ServerEncryption[i] = replace_Ones[r.Next(replace_Ones.Length)];
}
Console.WriteLine(ServerEncryption);
dotnetFiddle
What I have
A list of objects with Id, DateStart and DateFinish.
[
{
Id: 1234567890,
DateStart: new DateTime(),
DateFinish: new DateTime(),
},
...
]
What I need to do
I need to validate if none of the dates overlap each other.
I'm not sure if overlap is passing the right meaning here, so here is some examples:
Invalid Entry
[
{
Id: 1,
DateStart: new DateTime().AddHours(1),
DateFinish: new DateTime().AddHours(3),
},
{
Id: 2,
DateStart: new DateTime().AddHours(2),
DateFinish: new DateTime().AddHours(4),
}
]
This list have an overlap because the time of id 2 is in the middle of id 1
A table to show better:
-------------------------------------------------------------
| 1 | 2 | 3 | 4 |
| DateStart1 | | DateFinish1 | |
| | DateStart2 | | DateFinish2 |
-------------------------------------------------------------
*overlap* *overlap*
Other Invalid Examples
-------------------------------------------------------------
| 1 | 2 | 3 | 4 |
| DateStart1 | | | DateFinish1 |
| | DateStart2 | DateFinish2 | |
-------------------------------------------------------------
*overlap* *overlap*
-------------------------------------------------------------
| 1 | 2 | 3 | 4 |
| DateStart1 | | | DateFinish1 | // This would be a full overlap
| DateStart2 | | | DateFinish2 | // And it's also Invalid
-------------------------------------------------------------
*overlap* *overlap*
-------------------------------------------------------------
| 1 | 2 | 3 | 4 |
| | DateStart1 | | DateFinish1 | // Same as first example
| DateStart2 | | DateFinish2 | | // But "inverted"
-------------------------------------------------------------
*overlap* *overlap*
Valid Entry
[
{
Id: 1,
DateStart: new DateTime().AddHours(1),
DateFinish: new DateTime().AddHours(2),
},
{
Id: 2,
DateStart: new DateTime().AddHours(2),
DateFinish: new DateTime().AddHours(4),
}
]
A table to show better:
-------------------------------------------------------------
| 1 | 2 | 3 | 4 |
| DateStart1 | DateFinish1 | | |
| | DateStart2 | | DateFinish2 |
-------------------------------------------------------------
*not overlap*
And you can also have DateStart and DateFinish that are the same value, which means it can start and end at the same time.
-------------------------------------------------------------
| 1 | 2 | 3 | 4 |
| DateStart1 | | | |
| DateFinish1 | | | |
| DateStart2 | | | DateFinish2 |
-------------------------------------------------------------
*not overlap*
What I have done so far:
I'm making a foreach loop, where item is each element, and using a where with the following expression:
myList.Any(
x => x.Id == item.Id
&&
(
(
item.DateStart <= x.DateStart
&&
item.DateFinish > x.DateStart
&&
item.DateFinish <= x.DateFinish
)
||
(
item.DateStart >= x.DateStart
&&
item.DateStart < x.DateFinish
&&
item.DateFinish > x.DateFinish
)
||
(
item.DateStart <= x.DateStart
&&
item.DateFinish >= x.DateFinish
)
)
)
My Question
Is this expression correct? I have tried it with a lot of data and it seems to be wrong sometimes.
I need to be certain that it will cover all edge cases.
If there is a better way of writing all this logic, it would help to, because this code looks to ugly and hard to understand for other people.
I would use the following code:
static bool IsOverlapping(IEnumerable<Range> list)
{
Range previousRange = null;
foreach (var currentRange in list.OrderBy(x => x.DateStart).ThenBy(x => x.DateFinish))
{
if (currentRange.DateStart > currentRange.DateFinish)
return true;
if (previousRange?.DateFinish > currentRange.DateStart)
return true;
previousRange = currentRange;
}
return false;
}
A quick and dirty version. Not very performant as is on large sets. But can be improved on.
https://dotnetfiddle.net/Widget/PEn2Lm
static void DetectOverlap(List<Range> l)
{
foreach(var r in l)
{
var overlap = l.Any(x => x.Id != r.Id
&& ((r.Start == x.Start && r.End == x.End)
|| (r.Start >= x.Start && r.Start < x.End)
|| (r.End > x.Start && r.End <= x.End)));
if(overlap)
{
Console.WriteLine("Overlap detected");
throw new Exception("Overlapping range detected");
}
}
Console.WriteLine("Clean ranges");
}
I tried to mirror your cases, but I'd suggest writing unit tests to full test all of your scenarios.
List<FooBar> bars = new List<FooBar>()
{
new FooBar() //end date is inside 3
{
Start = new DateTime(2001,12,1),
End = new DateTime(2002,5,15),
Id = 1
},
new FooBar() //fine
{
Start = new DateTime(2005,12,1),
End = new DateTime(2006,5,15),
Id = 2
},
new FooBar() //start date is inside 1
{
Start = new DateTime(2002,4,1),
End = new DateTime(2003,5,15),
Id = 3
},
new FooBar() //this one is fine
{
Start = new DateTime(2006,5,15),
End = new DateTime(2007,5,15),
Id = 4
},
new FooBar() //also fine
{
Start = new DateTime(2001,12,1),
End = new DateTime(2001,12,1),
Id = 5
},
};
And then a, to me at least, slightly easier to read / skim code snippet which seems to work perfectly:
var inside = bars.Where(w =>
bars.Where(outer => ((outer.Start < w.Start && outer.End > w.Start)
|| (outer.Start < w.End && outer.End > w.End)
|| (outer.Start == w.Start && outer.End == w.End)) && outer.Id != w.Id).Any()).ToList();
inside.ForEach(e => {
Console.WriteLine($"{e.Id}");
});
For actual use, I'd also test for Any or First, and not ToList, but this gives me the Ids for the console to check.
As for why I used this logic, it might prove faulty but my assumptions:
An overlapped start date is between another input's start and end,
or an item's end date is between another input's start and end dates,
or a start and end date matches another input's values exactly.
Additional tests (as with your provided code) I expect false positives due to the use of <= and >=.
For example, changing the method to include
bars.Where(outer => ((outer.Start <= w.Start && outer.End >= w.Start)
gives me false positives on 4 and 5
I have a single list of objects that I want to make unique combinations from.
The objects that I have are (CityObj);
public string City_Name;
public int Population;
double xcord;
double ycord;
double zcord;
The result would be a list that contains a new object (CityComboObj)
public string City_NameA;
public int PopulationA;
double xcordA;
double ycordA;
double zcordA;
public string City_NameB;
public int PopulationB;
double xcordB;
double ycordB;
double zcordB;
A sample of the dataset is as follows;
City1 | 3840 | 42 | -12 | 5
City2 | 39402 | 1 | 59 | -5
City3 | 5934 | 99 | -55 | 3
City4 | 12394 | -56 | 9 | 16
The resultant list of objects would look like;
City1 | 3840 | 42 | -12 | 5 City2 | 39402 | 1 | 59 | -5
City1 | 3840 | 42 | -12 | 5 City3 | 5934 | 99 | -55 | 3
City1 | 3840 | 42 | -12 | 5 City4 | 12394 | -56 | 9 | 16
City2 | 39402 | 1 | 59 | -5 City3 | 5934 | 99 | -55 | 3
City2 | 39402 | 1 | 59 | -5 City4 | 12394 | -56 | 9 | 16
City3 | 5934 | 99 | -55 | 3 City4 | 12394 | -56 | 9 | 16
As you can see its only the unique results returned.
Currently I am using a horribly inefficient apporach to do going this;
foreach (var element in CityListA)
{
if (!CityListB.Any(o => o.City_NameA == element.City_NameA && o.City_NameB == element.City_NameB))
{
if (!CityListB.Any(o => o.City_NameA == element.City_NameB && o.City_NameB == element.City_NameA))
{
CityListB.add(element)
}
}
}
In a nutshell the approach is to take two lists one full and one empty, compare each element of the full list with the empty list to see if it exists or the transpose exists and if it doesn't add it.
It works but it is slow, is there a better way of doing this?
Thanks
Your code needs some reengineering, for example:
internal class CityObjs : List<CityObj>
{
}
internal class CityObj
{
public string City_Name;
public int Population;
double xcord;
double ycord;
double zcord;
}
internal class CityComboObj
{
internal CityObj CityA ;
internal CityObj CityB ;
internal CityComboObj(CityObj A,CityObj B) { CityA=A ; CityB=B;}
}
internal class CityComboObjs: List<CityComboObj>
{
}
Assuming that the list CityObjs is initialized:
CityComboObjs = new CityComboObjs() ;
for (int i=0;i<CityObjs.Count-1;i++) for (int j=i+1;j<CityObjs.Count;j++)
CityComboObjs.Add(new CityComboObj(CityObjs[i],CityObjs[j]) ;
This actually works. Just replace simple integers with real objects. The idea of this code is to do it more efficiently, as you asked, without checking if the pair already exists. In your case myList[i] will return a CITY object
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var myList = (new []{1 , 2, 3, 4}).ToList();
var newList = new List<ListObject>();
var count = myList.Count;
for (int i = 0; i < count - 1; i++)
{
for(int j = i + 1; j < count; j++)
{
newList.Add(new ListObject(){ I = myList[i], J = myList[j]});
}
}
newList.ForEach(x => Console.WriteLine(x));
}
class ListObject
{
public int I {get; set;}
public int J {get; set;}
public override string ToString()
{
return I + " - " + J;
}
}
}
Output
1 - 2
1 - 3
1 - 4
2 - 3
2 - 4
3 - 4
I am using C# RNGCryptoServiceProvider inside a using block
byte[] values = new byte[ammountToCreate];
using (RNGCryptoServiceProvider randomizer = new RNGCryptoServiceProvider())
{
randomizer.GetBytes(values);
}
The result values are then normalized to interval [0:15], therefore no modulo bias should affect the output.
Normalization is done in this way:
byte n = (byte)(this.maxRandomValue + 1);
// loop all element in the input value, an apply modulo.
for (int i = 0; i < values.Length; i++)
{
values[i] = (byte)(values[i] % n);
}
Where maxRandomValue is equal to 15.
However, from the first test, the output seems to be not uniform.
I have generated 2916 numbers, and this is the values distribution:
+-------+--------------+
| Value | % On Total |
+-------+--------------+
| 0 | 6.52% |
| 1 | 5.90% |
| 2 | 6.10% |
| 3 | 6.34% |
| 4 | 5.73% |
| 5 | 6.89% |
| 6 | 5.49% |
| 7 | 6.86% |
| 8 | 5.66% |
| 9 | 5.97% |
| 10 | 6.58% |
| 11 | 6.04% |
| 12 | 6.48% |
| 13 | 5.97% |
| 14 | 7.17% |
| 15 | 6.31% |
+-------+--------------+
As you can see:
6 --> 5.49% of generated numbers
14 --> 7.17% of generated numbers
My fear is that maybe I have generated just few numbers and with bigger volumes the distribution becomes uniform.
Or, RNGCryptoServiceProvider inside a using is not working as expected.
Do you have any idea?
I have a gave, which, when it's finished, has a table of players and their scores.
On the other hand i have a virtual pot of money that i want to distribute among these winners. I'm looking for a SQL query or piece of C# code to do so.
The descending sorted table looks like this:
UserId | Name | Score | Position | % of winnings | abs. winnings $
00579 | John | 754 | 1 | ? | 500 $
98983 | Sam | 733 | 2 | ? | ?
29837 | Rick | 654 | 3 | ? | ? <- there are 2 3rd places
21123 | Hank | 654 | 3 | ? | ? <- there are 2 3rd places
99821 | Buck | 521 | 5 | ? | ? <- there is no 4th, because of the 2 3rd places
92831 | Joe | 439 | 6 | ? | ? <- there are 2 6rd places
99281 | Jack | 439 | 6 | ? | ? <- there are 2 6rd places
12345 | Hal | 412 | 8 | ? | ?
98112 | Mick | 381 | 9 | ? | ?
and so on, until position 50
98484 | Sue | 142 | 50 | ? | 5 $
Be aware of the double 3rd and 6th places.
Now i want to distribute the total amount of (virtual) money ($ 10,000) among the first 50 positions. (It would be nice if the positions to distribute among (which is now 50) can be a variable).
The max and min amount (for nr 1 and nr 50) are fixed at 500 and 5.
Does anyone have a good idea for a SQL query or piece of C# code to fill the columns with % of winnings and absolute winnings $ correctly?
I prefer to have a distribution that looks a bit logarithmic like this: (which makes that the higher positions get relatively more than the lower ones).
.
|.
| .
| .
| .
| .
| .
| .
| .
| .
I haven't done SQL since 1994, but I like C# :-). The following might suit, adjust parameters of DistributeWinPot.DistributeWinPot(...) as required:
private class DistributeWinPot {
private static double[] GetWinAmounts(int[] psns, double TotWinAmounts, double HighWeight, double LowWeight) {
double[] retval = new double[psns.Length];
double fac = -Math.Log(HighWeight / LowWeight) / (psns.Length - 1), sum = 0;
for (int i = 0; i < psns.Length; i++) {
sum += retval[i] = (i == 0 || psns[i] > psns[i - 1] ? HighWeight * Math.Exp(fac * (i - 1)) : retval[i - 1]);
}
double scaling = TotWinAmounts / sum;
for (int i = 0; i < psns.Length; i++) {
retval[i] *= scaling;
}
return retval;
}
public static void main(string[] args) {
// set up dummy data, positions in an int array
int[] psns = new int[50];
for (int i = 0; i < psns.Length; i++) {
psns[i] = i+1;
}
psns[3] = 3;
psns[6] = 6;
double[] WinAmounts = GetWinAmounts(psns, 10000, 500, 5);
for (int i = 0; i < psns.Length; i++) {
System.Diagnostics.Trace.WriteLine((i + 1) + "," + psns[i] + "," + string.Format("{0:F2}", WinAmounts[i]));
}
}
}
Output from that code was:
1,1,894.70
2,2,814.44
3,3,741.38
4,3,741.38
5,5,614.34
6,6,559.24
7,6,559.24
8,8,463.41
9,9,421.84
10,10,384.00
11,11,349.55
12,12,318.20
13,13,289.65
14,14,263.67
15,15,240.02
16,16,218.49
17,17,198.89
18,18,181.05
19,19,164.81
20,20,150.03
21,21,136.57
22,22,124.32
23,23,113.17
24,24,103.02
25,25,93.77
26,26,85.36
27,27,77.71
28,28,70.74
29,29,64.39
30,30,58.61
31,31,53.36
32,32,48.57
33,33,44.21
34,34,40.25
35,35,36.64
36,36,33.35
37,37,30.36
38,38,27.64
39,39,25.16
40,40,22.90
41,41,20.85
42,42,18.98
43,43,17.27
44,44,15.72
45,45,14.31
46,46,13.03
47,47,11.86
48,48,10.80
49,49,9.83
50,50,8.95
Then how about this?
Select userid, log(score),
10000 * log(score) /
(Select Sum(log(score))
From TableName
Where score >=
(Select Min(score)
from (Select top 50 score
From TableName
Order By score desc) z))
From TableName
Order By score desc