Skip to content

Instantly share code, notes, and snippets.

@miketwo
Created December 13, 2020 21:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save miketwo/8e51714f260413954c183fb5346ba44f to your computer and use it in GitHub Desktop.
Save miketwo/8e51714f260413954c183fb5346ba44f to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'set'
# I have a group of 65 people that is divided into 10 subgroups. From the group of 65 I want to generate 17 teams. 14 teams of 4 people, 3 teams of 3.
# - First restriction: Each team must contain no more than 1 person from one of the subgroups. For example, a correct team would be {1, 2, 3, 4}, {2, 5, 6, 3}, and an incorrect team {2, 2, 5, 8} or {10, 2, 5, 2}. (the numbers 1-10 standing for the 10 subgroups)
# - Second restriction: I have to generate team combinations for multiple weeks, whereby people cannot be matched to a person they have been on a team with in the past. So if person A and B where in the same team in week 1, they cannot be matched up in any of the following weeks
# - Third (optional) restriction: The group contains of 22 males and 43 females. Besides the first two restrictions, it would be ideal to keep the male/female ration the same across all the teams. However, this might be hard to sustain after a few rounds, so it's not a priority. It would just be nice if the program maximised this.
class Person
attr_accessor :name
attr_accessor :group
attr_accessor :sex
def initialize(name, group, sex=:male)
@name = name
@group = group
@sex = sex
@former = [].to_set
end
def to_s
return "#{@name}, #{@group}, #{@sex}"
end
def add_former_teammate(other)
if other.name == self.name
return
end
if @former.member?(other)
raise "Error!"
end
@former = @former.add(other)
end
def valid_teammate?(other)
return @group != other.group && @former.none? {|f| f.name == other.name }
end
def show_former()
puts "[" + @former.map { |e| e.name }.join(",") + "]"
end
end
def fillCurrentTeam(person, notInGroup, currentGroup, team_size)
notInGroup.delete(person)
currentGroup.append(person)
# puts "Adding #{person}. Length is #{currentGroup.length}"
if currentGroup.length >= team_size
return currentGroup, notInGroup
end
for candidate in notInGroup
if currentGroup.all? {|p| p.valid_teammate?(candidate) }
return fillCurrentTeam(candidate, notInGroup, currentGroup, team_size)
end
end
return currentGroup, notInGroup
end
def create_teams(notInGroup, num_teams, team_size)
teams = []
while ! notInGroup.empty? && teams.length < num_teams
currentTeam = []
person = notInGroup.pop
currentTeam, notInGroup = fillCurrentTeam(person, notInGroup, currentTeam, team_size)
teams.append(currentTeam)
end
return teams, notInGroup
end
def team_stats(team)
fempercent = team.count { |m| m.sex == :female }.to_f / team.length.to_f * 100
# return "#{fempercent.round(2)}%% female"
return fempercent
end
def display(teams, verbose=false)
puts "#{teams.length} Teams"
puts "#{teams.flatten.length} People"
if verbose
puts "-"*40
for team in teams
puts "[" + team.map { |e| e.name }.join(",") + "] "
end
end
end
def update_restrictions(teams)
for team in teams
for person in team
team.map { |other| person.add_former_teammate(other) }
end
end
end
def print_and_flush(str)
print str
$stdout.flush
end
def create_and_display_teams(people)
backup = people.dup()
teams = []
remaining = []
loop do
teams, remaining = create_teams(people, 14, 4)
tmp, remaining = create_teams(remaining, 3, 3)
teams += tmp
#print_and_flush "."
break if remaining.length == 0 #&& teams.none? {|team| team_stats(team) < 1.0 || team_stats(team) > 99.0}
people = backup.dup().shuffle
end
display(teams, true)
puts "#{remaining.length} remaining"
puts "="*40
update_restrictions(teams)
return (teams + remaining).flatten.shuffle
end
def create_people()
people = []
(1..65).each do |cnt|
people.append(Person.new(cnt, cnt%10+1))
end
people.shuffle!
return people
end
def main
people = create_people()
(1..10).each do |week|
puts "Week #{week}"
people = create_and_display_teams(people)
end
end
if __FILE__ == $0
main()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment