Here's an array:
scramble = [
"R", "U", "R' ", "U' ", "L", "L' ", "L2", "B", "B' ",
"B2", "F", "F' ", "F2", "R2", "U2", "D", "D' ", "D2"
]
I want to shuffle it with a condition such that "R", "R' ", and "R2" are not together while shuffled, and similar for other letters.
scramble.shuffle does shuffle it, but how can I set such conditions?
Lets derive a generic solution.
Given a 2D array of groups of elements, return an array of shuffled elements such that no two consecutive elements belong to same group.
For example:
all_groups = [
["R", "R'", "R2" ],
["L", "L'", "L2" ],
["U", "U'", "U2" ],
["D", "D'", "D2" ],
["F", "F'", "F2" ],
["B", "B'", "B2" ]
]
Expected output:
["F'", "U'", "R'", "L2", "R2", "D2", "F2", "R", "L", "B", "F", "L'", "D'", "B'", "U2", "B2", "U", "D"]
TL;DR Code:
all_groups = [
["R", "R'", "R2" ],
["L", "L'", "L2" ],
["U", "U'", "U2" ],
["D", "D'", "D2" ],
["F", "F'", "F2" ],
["B", "B'", "B2" ]
]
ans = []
total = all_groups.map(&:length).reduce(:+)
# Initial shuffling
all_groups = all_groups.each { |i| i.shuffle! }.shuffle
until all_groups.empty?
# Select and pop last group
last_group = all_groups.pop
# Insert last element to our ans, and remove from group
ans.push(last_group.pop)
total -= 1
# Shuffle remaining groups
all_groups.shuffle!
# Check if any group has reached critical state
length_of_longest_group = all_groups.reduce(0) { |len, i| [len, i.length].max }
if length_of_longest_group * 2 == total + 1
# Move that group to last
# This ensures that next element picked is from this group
longest_group = all_groups.detect { |i| i.length == length_of_longest_group }
all_groups.delete(longest_group)
all_groups.push(longest_group)
end
# Insert the last group at first place. This ensures that next element
# is not from this group.
all_groups.unshift(last_group) unless last_group.empty?
end
puts ans.inspect
Examples:
all_groups = [
["R", "R'", "R2" ],
["L", "L'", "L2" ],
["U", "U'", "U2" ],
["D", "D'", "D2" ],
["F", "F'", "F2" ],
["B", "B'", "B2" ]
]
# Output 1:
ans = ["B'", "U'", "L'", "U2", "R", "B2", "F2", "R2", "D2", "L2", "D", "R'", "U", "F'", "D'", "L", "F", "B"]
# Output 2:
ans = ["U'", "R", "D", "R'", "U", "D2", "B2", "D'", "L", "B", "L2", "B'", "U2", "F'", "L'", "F", "R2", "F2"]
# Output 3:
ans = ["B", "D", "R'", "D'", "B'", "R", "F2", "L", "D2", "B2", "F'", "R2", "U'", "F", "L'", "U2", "L2", "U"]
# Output 4:
ans = ["U'", "F'", "R2", "B2", "D", "L2", "B'", "U", "R", "B", "R'", "L'", "D'", "U2", "F", "D2", "F2", "L"]
# Output 5:
ans = ["U2", "F2", "L'", "F'", "R'", "F", "D'", "B2", "D2", "L", "B", "D", "L2", "B'", "R", "U", "R2", "U'"]
all_groups = [
['a', 'aa', 'aaa', 'A', 'AA', 'AAA'],
['b', 'bb', 'bbb', 'B', 'BB', 'BBB'],
['c', 'cc', 'ccc', 'C', 'CC', 'CCC']
]
ans = ["c", "AAA", "B", "ccc", "bbb", "C", "AA", "CC", "aa", "BB", "CCC", "bb", "cc", "A", "BBB", "a", "b", "aaa"]
all_groups = [
['r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8'],
['b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8']
]
# Output1:
ans = ["r2", "b7", "r1", "b5", "r7", "b6", "r3", "b8", "r4", "b3", "r5", "b1", "r6", "b4", "r8", "b2"]
# Output2:
ans = ["b6", "r8", "b2", "r1", "b4", "r2", "b8", "r7", "b3", "r4", "b5", "r5", "b7", "r3", "b1", "r6"]
Explanation:
To begin with, lets shuffle the input data. First we shuffle each group, then we shuffle the outer array.
all_groups = all_groups.each { |i| i.shuffle! }.shuffle
Now our array looks like this:
[["B", "B2", "B'"],
["F'", "F", "F2"],
["L", "L2", "L'"],
["R2", "R'", "R"],
["D", "D'", "D2"],
["U'", "U", "U2"]]
Now, if we traverse this array in column-major order, we get a prety decent shuffling of elements wherein no two consecutive elements belong to same group.
["B", "F'", "L", "R2", "D", "U'", "B2", "F", "L2", "R'", "D'", "U", "B'", "F2", "L'", "R", "D2", "U2"]
But only flaw in this is that all elements of a particular group are equidistant. Lets improve.
So we have a set of groups. Lets select any one of group and then select the last element from that group and add this element to our answer array. Now shuffle the 2D array and repeat until all elements are selected.
Also, we don't want any two consecutive elements to belong to same group, so we need to ensure that the next selection of element is from some other group. So how about this strategy:
Always pick the last group from our 2d array, and then once we shuffle we will ensure that this group is not the last group in 2d array.
In terms of psuedocode:
1. Select last group from 2d array.
2. Remove an element from this group.
3. Shuffle the order of other groups in 2d array
4. Insert the selected group at begining.
For example, lets start with this 2d array:
[["D", "D'", "D2"],
["U", "U'", "U2"],
["B2", "B", "B'"],
["F2", "F", "F'"],
["L'", "L", "L2"],
["R'", "R2", "R"]]
last group: ["R'", "R2", "R"]
Element removed: ("R")
Shuffle order of remaining groups of 2d array:
[["L'", "L", "L2"],
["B2", "B", "B'"],
["D", "D'", "D2"],
["U", "U'", "U2"],
["F2", "F", "F'"]]
2D array after inserting the popped group(the group from which we extracted an element)
[["R'", "R2"],
["L'", "L", "L2"],
["B2", "B", "B'"],
["D", "D'", "D2"],
["U", "U'", "U2"],
["F2", "F", "F'"]]
Now, since our logic selects the last group and then inserts it in the begining, we are insured that two elements of same group will never be picked in two consecutive turns.
A catch: There can be a possibility that there is a group which never reaches the last position, hence this group will never shrink, and its element will be forced to be picked consecutively once the number of elements is too less.
For example observe the output of 4 iterations of above algorithm on below input:
[['a', 'aa', 'aaa', 'A', 'AA', 'AAA'],
['b', 'bb', 'bbb', 'B', 'BB', 'BBB'],
['c', 'cc', 'ccc', 'C', 'CC', 'CCC']]
# Output 1: in this 'a' & 'aaa' are together
["CC", "bb", "C", "BB", "aa", "bbb", "AA", "CCC", "b", "c", "B", "cc", "A", "BBB", "AAA", "ccc", "a", "aaa"]
# Output 2: in this 'b' & 'BB' are together
["cc", "A", "B", "c", "AAA", "C", "a", "ccc", "bbb", "CCC", "aaa", "bb", "CC", "aa", "BBB", "AA", "b", "BB"]
# Output 3: this is perfect
["CCC", "BB", "a", "c", "BBB", "aa", "bbb", "A", "bb", "ccc", "B", "CC", "AAA", "b", "AA", "cc", "aaa", "C"]
# Output 4: in this 'c' & 'cc' are together
["CC", "bb", "AA", "b", "aa", "BBB", "aaa", "bbb", "CCC", "B", "A", "BB", "C", "a", "ccc", "AAA", "c", "cc"]
So, there are chances that when ratio of number of elements in a group to total no of elements increases, two elements of same group can be clubbed together. Hmm, lets improve further:
Since the group with maximum number of elements has most probability of having two elements combined consecutively, lets study such groups. i.e. group with maximum number of elements.
Lets say, there is a group with X number of elements in it. So what is the minimum number of other type of elements required that none of the X elements are consecutive? Simple right: insert a different element between each pair of element of this group.
x * x * x * x * x * x
So we realise, that if group has X elements it requires atleast (X-1) other type of elements so that no two elements of this group get consecutive.
Mathematically the condition can be represented as:
number_of_elements_in_longest_group == number_of_other_type_of_elements + 1
=> number_of_elements_in_longest_group == (total_number_of_elements - number_of_elements_in_longest_group) + 1
=> number_of_elements_in_longest_group * 2 == total_number_of_elements + 1
Going back to our algorithm, lets add a condition, that at any point of time, if number of remaining items is 1 less than the number of elements in largest group then we have to ensure that next element picked is from this largest group
Juksefantomet sent me this solution in discord, since he can't post here due to an account lock.
The below codeblock contains an alternative approach on how to tackle the problem at hand. This contains a fragmented solution to understand the steps on how you would normally approach a complex problem like the one presented above.
going through the various steps you can see how each condition has to be known in advance and specified to the point where your final array is not "illegal".
#illegal_order = ['1','2','3','4','5','6','7','8','9']
puts #illegal_order.count
puts "#{#illegal_order}"
#foo = []
# traverse the original array and append a random value from that into foo array
# this can of course and should be done in a loop where you check for duplicates
# this is done below in this example, fragmented to see the individual action
(0..#illegal_order.count).each do |add_value|
#temp = #illegal_order[rand(#illegal_order.count)]
unless #foo.count == #illegal_order.count
#foo.push(#temp)
end
end
# making sure it contains everything in the original array
#foo.each do |bar|
unless #illegal_order.include?(bar)
#approve = false
puts "Errored generation!"
end
end
# defining patterns, this can of course be extracted by the original array and be done alot cleaner
# but printing it out to better break it down
#pattern1 = [#illegal_order[0],#illegal_order[1],#illegal_order[2]]
#pattern2 = [#illegal_order[3],#illegal_order[4],#illegal_order[5]]
#pattern3 = [#illegal_order[6],#illegal_order[7],#illegal_order[8]]
# Let us step through the new array and use case to check for various conditions that would flag the new array as invalid
#foo.each do |step|
# setting a temp pattern based on current state
#temp_pattern = [#foo[step.to_i-1],#illegal_order[step.to_i],#illegal_order[step.to_i+1]]
case step
when #temp_pattern == #pattern1
#illegalpatterns = true
when #temp_pattern == #pattern2
#illegalpatterns = true
when #temp_pattern == #pattern3
#illegalpatterns = true
end
# checking the foo array if it contains duplicates, if yes, set conditional to true
#illegal_order.each do |count|
if #foo.count(count) > 1
#duplicates = true
end
end
end
# printing out feedback based on duplicates or not, this is where you rescramble the array if duplicate found
(#duplicates == true) ? (puts "dupes found") : (puts "looks clear. no duplicates")
# printing out feedback based on illegal patterns or not, this is where you rescramble the array if duplicate found
(#illegalpatterns == true) ? (puts "illegal patterns found") : (puts "looks clear, no illegal patterns")
puts "#{#foo}"
I want to replace all A characters present in an array with 15 using Swift 3.
Example array:
["4", "5", "6", "A", "A", "Q", "A"]
Desired result:
["4", "5", "6", "15", "15", "Q", "15"]
map to the rescue:
var a = ["4", "5", "6", "A", "A", "Q", "A"]
a = a.map({ $0 == "A" ? "15" : $0 })
print(a)// ["4", "5", "6", "15", "15", "Q", "15"]
EDIT: After error screenshot:
You have an array of characters and hence the above code is not working. Also, remember "15" is two characters and not one character. Hence, I have replaced character 'A' with string "15" and mapped it to an array of strings, instead
let player1 = "456AAQA"
var player1Cards = Array(player1.characters) // ["4", "5", "6", "A", "A", "Q", "A"]
var player1CardsStrings = player1Cards.map{$0 == "A" ? "15" : String($0)}
player1CardsStrings // ["4", "5", "6", "15", "15", "Q", "15"]
Tested on Playground.
Because your question is lacking information that you didn't gave at first, here is what you can do.
"for loop": You iterate and replace the value if needed.
That's a logic you could apply on almost all languages.
var array1 = ["4", "5", "6", "A", "A", "Q", "A"]
for index in 0 ... array1.count-1
{
if array1[index] == "A"
{
array1[index] = "15"
}
}
print("array1: \(array1)")
"for each loop": You iterate and replace the value if needed.
That's a logic you could apply on almost all languages (maybe less languages that the previous one)
var array2 = ["4", "5", "6", "A", "A", "Q", "A"]
for (index, object) in array2.enumerated()
{
if object == "A"
{
array2[index] = "15"
}
}
print("array2: \(array2)")
"map": the "map" iterate for you (here is the important part behind the magic), you check the value and replace the value if needed. The $0 represent the "current item".
Here is a specificity.
var array3 = ["4", "5", "6", "A", "A", "Q", "A"]
array3 = array3.map({
if $0 == "A"
{
return "15"
}
return $0
})
print("array3: \(array3)")
"map": the "map" iterate for you, you check the value and replace the value if needed with a ternary if test.
var array4 = ["4", "5", "6", "A", "A", "Q", "A"]
array4 = array4.map({$0 == "A" ? "15" : $0})
print("array4: \(array4)")
I'm gave 4 ways (I've could also have explicit more the map() with explicit closure, from the simplest to the more complicated. We can't know if you don't show your attempts where you are are stucked. Is it for loop? The basic algorithms?
Swift advanced user may be more fond of the last one, but for beginners, it's quite complex and "magic". So when they want to change it a little for a different test, they never know what to do.
Side note: I'm not a Swift developer, more an Objective-C, so it may be lacking of checks. This answers is to show different approaches, how you go from a "verbose" to a less "verbose" code, but that you need to master anyway. Even if you have issue with map(), you can "bypass" it and do it manually.
let values = ["4", "5", "6", "A", "A", "Q", "A"]
let mappedvalues = values.map({ (value: String) -> String in
if value != "A" {
return value
}
return "15"
})
I am trying to combine two two arrays to make a full deck of cards that looks like so:
[{card: "A", suit: "d"}, {card: "A", suit: "c"}, {card: "A", suit: "s"}, {card: "A", suit: "h"}, {card: "2", suit: "d"}.....]
.... this is what I have so far:
function newDeck(ranks, suits){
var ranks = [ "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
var suits = ["d", "c", "s", "h"]
var deck= []
for (i = 0; i < suits.length; i++) {
for (j = 0; j < ranks.length; j++) {
this.deck[ranks.length*i+j] = new Card(ranks[j], suits[i]);
}
} console.log(newDeck)
}
Using Array.forEach you can do the following:
var ranks = [ "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"];
var suits = ["d", "c", "s", "h"];
var deck= [];
suits.forEach(function(suit){
ranks.forEach(function(rank){
deck.push(new Card(rank, suit));
})
});
EDIT: and in case you haven't written the Card method yet:
function Card(rank, suit){
this.card = rank;
this.suit = suit;
}
If you combine the two arrays you will have an array:
["A","2","3","4","5","6","7","8","9","10","J","Q","K","d","c","s","h"]
which does not represent a full deck of card, whereas using an embedded for loop to print out card as you are now will so I don't think you just want to append one array to the other. Can you provide more context on what you want to do with the array?
However, to answer your question: if you want to append two arrays you can use:
var appendedArray = ranks.concat(suits);
which will result in the aforementioned array above
pertaining to your updated question: you are called "new Card(ranks[j], suits[i]);" have you made a Card constructor so that this is valid? if so, the code should be correct if the constructor matches how you are using it. Posting the code for the constructor would be helpful along with an update of what issue you are facing