Find row numbers in a binary array with a certain code - matlab

I'm building a program that plays Connect 4, and one of the things to check for is cases where your opponent has the following board positions:
[0,1,1,0,0] or [0,1,0,1,0] or [0,0,1,1,0]
where your opponent is one move away from having three pieces in a row with a blank on either side. If you don't fill one of the middle elements on your next move, your opponent can go there and force a checkmate.
What I have is a board of 42 squares, numbered 1:42. And I created a matrix called FiveCheck, where each row maps to five consecutive board positions. For example:
FiveCheck(34,:) = [board(7),board(14),board(21),board(28),board(35)];
FiveCheck(35,:) = [board(14),board(21),board(28),board(35),board(42)];
are two of the diagonals of the board.
I can test for the possible checkmate with
(sum(FiveCheck(:,2:4),2) == 2 + sum(FiveCheck,2) == 2) == 2
And that gives me a vector with 1's indicating that the corresponding FiveCheck row has a possible checkmate. Let's say the 34th element of that vector has a 1, and the pattern for that diagonal (from the example given above) is [0,0,1,1,0]. How do I return 14, the board position I should move to?
Another separate example, if the 35th element of that vector has a 1, and the pattern for that diagonal is [0,1,0,1,0], how do I return 28?
EDIT: I just realized this is impossible without some sort of a map. So I created FiveMap, a matrix the same size of FiveCheck, with the same formulas except the word "board" is removed. For example:
FiveMap(34,:) = [(7),(14),(21),(28),(35)];
FiveMap(35,:) = [(14),(21),(28),(35),(42)];

Since you are dealing with binary vectors of size 5, a very efficient solution might be the use of a look up table.
Consider board to be a binary matrix. you can filter it with 4 filters (of length 5), representing horizontal, vertical and two diagonals to identify possible positions you are looking for. Then, for specious locations, you can extract the 5 binary bits and use a look up table of size 32 to get the offset to the position where you should place your piece.
a small example:
% construct LUT
LUT = zeros(32,2); % dx and dy offsets for each entry
LUT(12,:) = [ 1 0 ]; % covering the case [0 1 1 0 0] - put piece 1 to the right of center
% continue constructing LUT here...
horFilt = ones(1, 5);
resp = imfilter( board, horFilt ); % need to think about board''s boundaries here...
[yy xx] = find( resp == 2 ); all locations where filter caught 2 out of 5
pat = board( yy, xx + [ -2 1 0 1 2] ); % I assume here only one location was found
pat = bin2dec( '0'+char( pat ) ); % convert binary pattern to decimal entry
board( yy + LUT(pat,2) , xx + LUT(pat, 1) ) = ... ; % your move here...

Related

Recognizing poker hands from a 2D matrix of values

I have a 1000 x 5 sorted matrix with numbers from 1-13. Each number denotes the numerical value of a playing card. The Ace has the value 1, then the numbers 2 through 10 follow, then the Jack has value 11, Queen with value 12 and King with value 13. Therefore, each row of this matrix constitutes a poker hand. I am trying to create a program that recognizes poker hands using these cards that are enumerated in this way.
For example:
A = [1 1 2 4 5; 2 3 4 5 7; 3, 3, 5, 5, 6; 8, 8, 8, 9, 9]
Therefore, in this matrix A, the first row has a pair (1,1). The second row has high card (7), the third row has two pair ((3,3) and (5,5)) and the last one is a full house (Pair of 9s and 3 of a kind (8).
Is there a good way to do this in MATLAB?
bsxfun won't work for this situation. This is a counting problem. It's all a matter of counting what you have. Specifically, poker hands deal with counting up how much of each card you have, and figuring out the right combination of counts to get a valid hand. Here's a nice picture that shows us every possible poker hand known to man:
Source: http://www.bestonlinecasino.tips
Because we don't have the suits in your matrix, I'm going to ignore the Royal Flush, Straight Flush and the Flush scenario. Every hand you want to recognize can be chalked up to taking a histogram of each row with bins from 1 to 13, and determining if (in order of rank):
Situation #1: A high hand - all of the bins have a bin count of exactly 1
Situation #2: A pair - you have exactly 1 bin that has a count of 2
Situation #3: A two pair - you have exactly 2 bins that have a count of 2
Situation #4: A three of a kind - you have exactly 1 bin that has a count of 3
Situation #5: Straight - You don't need to compute the histogram here. Simply sort your hand, and take neighbouring differences and make sure that the difference between successive values is 1.
Situation #6: Full House - you have exactly 1 bin that has a count of 2 and you have exactly 1 bin that has a count of 3.
Situation #7: Four of a kind - you have exactly 1 bin that has a count of 4.
As such, find the histogram of your hand using histc or histcounts depending on your MATLAB version. I would also pre-sort your hand over each row to make things simpler when finding a straight. You mentioned in your post that the matrix is pre-sorted, but I'm going to assume the general case where it may not be sorted.
As such, here's some pre-processing code, given that your matrix is in A:
Asort = sort(A,2); %// Sort rowwise
diffSort = diff(Asort, 1, 2); %// Take row-wise differences
counts = histc(Asort, 1:13, 2); %// Count each row up
diffSort contains column-wise differences over each row and counts gives you a N x 13 matrix where N are the total number of hands you're considering... so in your case, that's 1000. For each row, it tells you how many of a particular card has been encountered. So all you have to do now is go through each situation and see what you have.
Let's make an ID array where it's a vector that is the same size as the number of hands you have, and the ID tells you which hand we have played. Specifically:
* ID = 1 --> High Hand
* ID = 2 --> One Pair
* ID = 3 --> Two Pairs
* ID = 4 --> Three of a Kind
* ID = 5 --> Straight
* ID = 6 --> Full House
* ID = 7 --> Four of a Kind
As such, here's what you'd do to check for each situation, and allocating out to contain our IDs:
%// To store IDs
out = zeros(size(A,1),1);
%// Variables for later
counts1 = sum(counts == 1, 2);
counts2 = sum(counts == 2, 2);
counts3 = sum(counts == 3, 2);
counts4 = sum(counts == 4, 2);
%// Situation 1 - High Hand
check = counts1 == 5;
out(check) = 1;
%// Situation 2 - One Pair
check = counts2 == 1;
out(check) = 2;
%// Situation 3 - Two Pair
check = counts2 == 2;
out(check) = 3;
%// Situation 4 - Three of a Kind
check = counts3 == 1;
out(check) = 4;
%// Situation 5 - Straight
check = all(diffSort == 1, 2);
out(check) = 5;
%// Situation 6 - Full House
check = counts2 == 1 & counts3 == 1;
out(check) = 6;
%// Situation 7 - Four of a Kind
check = counts4 == 1;
out(check) = 7;
Situation #1 basically checks to see if all of the bins that are encountered just contain 1 card. If we check for all bins that just have 1 count and we sum all of them together, we should get 5 cards.
Situation #2 checks to see if we have only 1 bin that has 2 cards and there's only one such bin.
Situation #3 checks if we have 2 bins that contain 2 cards.
Situation #4 checks if we have only 1 bin that contains 3 cards.
Situation #5 checks if the neighbouring differences for each row of the sorted result are all equal to 1. This means that the entire row consists of 1 when finding neighbouring distances. Should this be the case, then we have a straight. We use all and check every row independently to see if all values are equal to 1.
Situation #6 checks to see if we have one bin that contains 2 cards and one bin that contains 3 cards.
Finally, Situation #7 checks to see if we have 1 bin that contains 4 cards.
A couple of things to note:
A straight hand is also technically a high hand given our definition, but because the straight check happens later in the pipeline, any hands that were originally assigned a high hand get assigned to be a straight... so that's OK for us.
In addition, a full house can also be a three of a kind because we're only considering the three of a kind that the full house contains. However, the later check for the full house will also include checking for a pair of cards, and so any hands that were assigned a three of a kind will become full houses eventually.
One more thing I'd like to note is that if you have an invalid poker hand, it will automatically get assigned a value of 0.
Running through your example, this is what I get:
>> out
out =
2
1
3
6
This says that the first hand is a one pair, the next hand is a high card, the next pair is two pairs and the last hand is a full house. As a bonus, we can actually output what the strings are for each hand:
str = {'Invalid Hand', 'High Card', 'One Pair', 'Two Pair', 'Three of a Kind', 'Straight', 'Full House', 'Four of a Kind'};
hands = str(out+1);
I've made a placeholder for the invalid hand, and if we got a legitimate hand in our vector, you simply have to add 1 to each index to access the right hand. If we don't have a good hand, it'll show you an Invalid Hand string.
We get this for the strings:
hands =
'One Pair' 'High Card' 'Two Pair' 'Full House'

Calculating the Local Ternary Pattern of an image?

I am calculating the Local Ternary Pattern of an image. My code is given below. Am I going in the right direction or not?
function [ I3 ] = LTP(I2)
m=size(I2,1);
n=size(I2,2);
for i=2:m-1
for j=2:n-1
J0=I2(i,j);
I3(i-1,j-1)=I2(i-1,j-1)>J0;
end
end
I2 is the image LTP is applied to.
This isn't quite correct. Here's an example of LTP given a 3 x 3 image patch and a threshold t:
(source: hindawi.com)
The range that you assign a pixel in a window to 0 is when the threshold is between c - t and c + t, where c is the centre intensity of the pixel. Therefore, because the intensity is 34 in the centre of this window, the range is between [29,39]. Any values that are beyond 39 get assigned 1 and any values that are below 29 get assigned -1. Once you determine the ternary codes, you split up the codes into upper and lower patterns. Basically, any values that get assigned a -1 get assigned 0 for upper patterns and any values that get assigned a -1 get assigned 1 for lower patterns. Also, for the lower pattern, any values that are 1 from the original window get mapped to 0. The final pattern is reading the bit pattern starting from the east location with respect to the centre (row 2, column 3), then going around counter-clockwise. Therefore, you should probably modify your function so that you're outputting both lower patterns and upper patterns in your image.
Let's write the corrected version of your code. Bear in mind that I will not give an optimized version. Let's get a basic algorithm working, and it'll be up to you on how you want to optimize this. As such, change your code to something like this, bearing in mind all of the stuff I talked about above. BTW, your function is not defined properly. You can't use spaces to define your function, as well as your variables. It will interpret each word in between spaces as variables or functions, and that's not what you want. Assuming your neighbourhood size is 3 x 3 and your image is grayscale, try something like this:
function [ ltp_upper, ltp_lower ] = LTP(im, t)
%// Get the dimensions
rows=size(im,1);
cols=size(im,2);
%// Reordering vector - Essentially for getting binary strings
reorder_vector = [8 7 4 1 2 3 6 9];
%// For the upper and lower LTP patterns
ltp_upper = zeros(size(im));
ltp_lower = zeros(size(im));
%// For each pixel in our image, ignoring the borders...
for row = 2 : rows - 1
for col = 2 : cols - 1
cen = im(row,col); %// Get centre
%// Get neighbourhood - cast to double for better precision
pixels = double(im(row-1:row+1,col-1:col+1));
%// Get ranges and determine LTP
out_LTP = zeros(3, 3);
low = cen - t;
high = cen + t;
out_LTP(pixels < low) = -1;
out_LTP(pixels > high) = 1;
out_LTP(pixels >= low & pixels <= high) = 0;
%// Get upper and lower patterns
upper = out_LTP;
upper(upper == -1) = 0;
upper = upper(reorder_vector);
lower = out_LTP;
lower(lower == 1) = 0;
lower(lower == -1) = 1;
lower = lower(reorder_vector);
%// Convert to a binary character string, then use bin2dec
%// to get the decimal representation
upper_bitstring = char(48 + upper);
ltp_upper(row,col) = bin2dec(upper_bitstring);
lower_bitstring = char(48 + lower);
ltp_lower(row,col) = bin2dec(lower_bitstring);
end
end
Let's go through this code slowly. First, I get the dimensions of the image so I can iterate over each pixel. Also, bear in mind that I'm assuming that the image is grayscale. Once I do this, I allocate space to store the upper and lower LTP patterns per pixel in our image as we will need to output this to the user. I have decided to ignore the border pixels where when we consider a pixel neighbourhood, if the window goes out of bounds, we ignore these locations.
Now, for each valid pixel that is within the valid borders of the image, we extract our pixel neighbourhood. I convert these to double precision to allow for negative differences, as well as for better precision. I then calculate the low and high ranges, then create a LTP pattern following the guidelines we talked about above.
Once I calculate the LTP pattern, I create two versions of the LTP pattern, upper and lower where any values of -1 for the upper pattern get mapped to 0 and 1 for the lower pattern. Also, for the lower pattern, any values that were 1 from the original window get mapped to 0. After, this, I extract out the bits in the order that I laid out - starting from the east, go counter-clockwise. That's the purpose of the reorder_vector as this will allow us to extract those exact locations. These locations will now become a 1D vector.
This 1D vector is important, as we now need to convert this vector into character string so that we can use bin2dec to convert the value into a decimal number. These numbers for the upper and lower LTPs are what are finally used for the output, and we place those in the corresponding positions of both output variables.
This code is untested, so it'll be up to you to debug this if it doesn't work to your specifications.
Good luck!

Matlab - submatrix for stiffness method

In order to use the stiffness method for trusses, I need to extract certain elements from a large global stiffness matrix.
Say I have a 9 x 9 matrix K representing a three-member truss. This means that the first 3 rows and columns correspond to the first node, the second set of three rows and columns with the second node, and the third with the third node. In the code is a vector zDisp that corresponds to each node that has zero displacement. On paper, a zero displacement of a node means you would cross out the rows and columns corresponding to that displacement, leaving you with a smaller and easier to work with K matrix. So if the first and third nodes have zero displacement, you would be left with a 3 x 3 matrix corresponding to the intersection of the middle three rows and the middle three columns.
I thought I could accomplish this one node at a time with a function like so:
function [ B ] = deleteNode( B, node )
%deleteNode removes the corresponding rows and vectors to a node that has
% zero deflection from the global stiffness matrix
% --- Problem line - this gets the first location in the matrix corresponding to the node
start = 3*node- 2;
for i = 0 : 2
B(start+i,:) = [];
B(:,start+i) = [];
end
end
So my main project would go something like
% Zero displacement nodes
zDisp = [1;
3;
];
% --- Create 9 x 9 global matrix Kg ---
% Make a copy of the global matrix
S = Kg;
for(i = 1 : length(zDisp))
S = deleteNode(S, zDisp(i));
end
This does not work because once the loop executes for node 1 and removes the first 3 rows and columns, the problem line in the function no longer works to find the correct location in the smaller matrix to find the node.
So I think this step needs to be executed all at once. I am thinking I may need to instead input which nodes are NOT zero displacement, and create a submatrix based off of that. Any tips on this? Been thinking on it awhile. Thanks all.
In your example, you want to remove rows/columns 1, 2, 3, 7, 8, and 9, so if zDisp=[1;3],
remCols=bsxfun(#plus,1:3,3*(zDisp-1))
If I understand correctly, you should just be able to first remove the columns given by zDisp:
S(remCols(:),:)=[]
then remove the rows:
S(:,remCols(:))=[]

Indices of constant consecutive values in a matrix, and number of constant values

I have a matrix with constant consecutive values randomly distributed throughout the matrix. I want the indices of the consecutive values, and further, I want a matrix of the same size as the original matrix, where the number of consecutive values are stored in the indices of the consecutive values. For Example
original_matrix = [1 1 1;2 2 3; 1 2 3];
output_matrix = [3 3 3;2 2 0;0 0 0];
I have struggled mightily to find a solution to this problem. It has relevance for meteorological data quality control. For example, if I have a matrix of temperature data from a number of sensors, and I want to know what days had constant consecutive values, and how many days were constant, so I can then flag the data as possibly faulty.
temperature matrix is number of days x number of stations and I want an output matrix that is also number of days x number of stations, where the consecutive values are flagged as described above.
If you have a solution to that, please provide! Thank you.
For this kind of problems, I made my own utility function runlength:
function RL = runlength(M)
% calculates length of runs of consecutive equal items along columns of M
% work along columns, so that you can use linear indexing
% find locations where items change along column
jumps = diff(M) ~= 0;
% add implicit jumps at start and end
ncol = size(jumps, 2);
jumps = [true(1, ncol); jumps; true(1, ncol)];
% find linear indices of starts and stops of runs
ijump = find(jumps);
nrow = size(jumps, 1);
istart = ijump(rem(ijump, nrow) ~= 0); % remove fake starts in last row
istop = ijump(rem(ijump, nrow) ~= 1); % remove fake stops in first row
rl = istop - istart;
assert(sum(rl) == numel(M))
% make matrix of 'derivative' of runlength
% don't need last row, but needs same size as jumps for indices to be valid
dRL = zeros(size(jumps));
dRL(istart) = rl;
dRL(istop) = dRL(istop) - rl;
% remove last row and 'integrate' to get runlength
RL = cumsum(dRL(1:end-1,:));
It only works along columns since it uses linear indexing. Since you want do something similar along rows, you need to transpose back and forth, so you could use it for your case like so:
>> original = [1 1 1;2 2 3; 1 2 3];
>> original = original.'; % transpose, since runlength works along columns
>> output = runlength(original);
>> output = output.'; % transpose back
>> output(output == 1) = 0; % see hitzg's comment
>> output
output =
3 3 3
2 2 0
0 0 0

Get the neighbors of a matrix element

I have a matrix and for each element I want to get the index of its surrounding elements. All these results have to be stored into a matrix in the following way. Each row of the matrix corresponds to a matrix element and each of the columns of this matrix contain s the neighbor indexes. For example, for a 4x4 matrix we will get a 16x8 result array. Some of the matrix elements do not have 8 neighbors.
There is an example, I think it is working, I there any way to avoid for loop?:
ElementNeighbors = [];
for n = 1:numel(Matrix)
NeighborsMask = [ n-1 n+1 n+size(matrix,1) n-size(Matrix,1) n-size(Matrix,1)-1 n-size(Matrix,1)+1 ...
n+size(Matrix,1)-1 n+size(Matrix,1)+1 ];
ElementNeighbors = [ElementNeighbors ; NeighborsMask ];
end
ElementNeighbors (ElementNeighbors ==0|ElementNeighbors <0) = NaN;
Given the linear indices of a matrix M(n,m), you can convince yourself that the top left neighbor of element M(i,j) = M(i-1, j-1) = M(i-1 + n * (j-2))
In "linear index" space that means the offset of this element is
-n-1
Doing this for all other locations, we find
-n-1 | -1 | n-1
-n | x | n => [-n-1, -n, -n+1, -1, +1, +n-1, +n, +n+1]
-n+1 | +1 | n+1
Thus you can create a vector offset with the above values (replacing n with the first dimension). For example, if M is (5x4), then
offset = [-6 -5 -4 -1 1 4 5 6];
You then create all the indices:
indices = bsxfun(#plus, (1:m*n), offset(:));
bsxfun is a cool shorthand for "do this function on these elements; where one element has a singleton dimension and the other doesn't, expand accordingly". You could do the same with repmat, but that creates unnecessary intermediate matrices (which can sometimes be very large).
That command will create a (8 x m*n) matrix of indices of all 8 neighbors, including ones that may not really be the neighbors... something you need to fix.
Several possible approaches:
pad the matrix before you start
don't care about wrapping, and just get rid of the elements that fall off the edge
create a mask for all the ones that are "off the edge".
I prefer the latter. "Off the edge" means:
going up in the top row
going left in the left column
going down in the bottom row
going right in the right column
In each of these four cases there are 3 indices that are 'invalid'. Their position in the above matrix can be determined as follows:
mask = zeros(size(M));
mask(:,1) = 1;
left = find(mask == 1);
mask(:,end) = 2;
right = find(mask == 2);
mask(1,:) = 3;
top = find(mask == 3);
mask(end,:) = 4;
bottom = find(mask == 4);
edgeMask = ones(8,m*n);
edgeMask(1:3, top) = 0;
edgeMask([1 4 6], left) = 0;
edgeMask([3 5 8], right) = 0;
edgeMask(6:8, bottom) = 0;
Now you have everything you need - all the indices, and the "invalid" ones. Without loops.
If you were feeling ambitious you could turn this into a cell array but it will be slower than using the full array + mask. For example if you want to find the average of all the neighbors of a value, you can do
meanNeighbor = reshape(sum(M(indices).*edgeMask, 1)./sum(edgeMask, 1), size(M));
EDIT re-reading your question I see you wanted a M*N, 8 dimension. My code is transposed. I'm sure you can figure out how to adapt it...
ATTRIBUTION #Tin helpfully suggested many great edits to the above post, but they were rejected in the review process. I cannot totally undo that injustice - but would like to record my thanks here.
EXTENDING TO DIFFERENT REGIONS AND MULTIPLE DIMENSIONS
If you have an N-dimensional image matrix M, you could find the neighbors as follows:
temp = zeros(size(M));
temp(1:3,1:3,1:3) = 1;
temp(2,2,2) = 2;
offsets = find(temp==1) - find(temp==2);
If you want a region that is a certain radius in size, you could do
sz = size(M);
[xx yy zz] = meshgrid(1:sz(1), 1:sz(2), 1:sz(3));
center = round(sz/2);
rr = sqrt((xx - center(1)).^2 + (yy - center(2)).^2 + (zz - center(3)).^2);
offsets = find(rr < radius) - find(rr < 0.001);
You can probably figure out how to deal with the problem of edges along the lines shown earlier for the 2D case.
Untested - please see if you notice any problems with the above.