func skillFactorListToScheduleStep(facs []factor.Factor, idx int) []sched.RunnableSchedule {
	steps := []sched.RunnableSchedule{}
	for _, f := range facs {
		step := sched.NewStep(f.UpdateMessage, idx)
		steps = append(steps, step)
	}

	return steps
}
// buildSkillFactorSchedule builds a full schedule that represents all the steps
// in a factor graph.
func buildSkillFactorSchedule(numPlayers int, sf skillFactors, loopMaxDelta float64) sched.RunnableSchedule {
	// Prior schedule initializes the skill priors for all players and updates
	// the performance
	priorSchedule := sched.NewSequence(
		sched.NewSequence(skillFactorListToScheduleStep(sf.skillPriorFactors, 0)...),
		sched.NewSequence(skillFactorListToScheduleStep(sf.skillToPerformanceFactors, 0)...),
	)

	// Loop schedule iterates until desired accuracy is reached
	var loopSchedule sched.RunnableSchedule

	if numPlayers == 2 {
		// In two player mode there is no loop, just send the performance
		// difference and the greater-than.
		loopSchedule = sched.NewSequence(
			sched.NewStep(sf.performanceToPerformanceDifferencFactors[0].UpdateMessage, 0),
			sched.NewStep(sf.greatherThanOrWithinFactors[0].UpdateMessage, 0),
		)
	} else {
		// Forward schedule updates the factor graph in one direction
		forwardSchedule := []sched.RunnableSchedule{}
		// ... and the backward schedule in the other direction
		backwardSchedule := []sched.RunnableSchedule{}

		for i := 0; i < numPlayers-2; i++ {
			forwardSteps := []sched.RunnableSchedule{
				sched.NewStep(sf.performanceToPerformanceDifferencFactors[i].UpdateMessage, 0),
				sched.NewStep(sf.greatherThanOrWithinFactors[i].UpdateMessage, 0),
				sched.NewStep(sf.performanceToPerformanceDifferencFactors[i].UpdateMessage, 2),
			}
			forwardSchedule = append(forwardSchedule, forwardSteps...)

			backwardSteps := []sched.RunnableSchedule{
				sched.NewStep(sf.performanceToPerformanceDifferencFactors[numPlayers-2-i].UpdateMessage, 0),
				sched.NewStep(sf.greatherThanOrWithinFactors[numPlayers-2-i].UpdateMessage, 0),
				sched.NewStep(sf.performanceToPerformanceDifferencFactors[numPlayers-2-i].UpdateMessage, 1),
			}
			backwardSchedule = append(backwardSchedule, backwardSteps...)
		}

		// Combine the backward and forward schedule so that they are run in
		// said order
		combinedForwardBackwardSchedule := sched.NewSequence(
			sched.NewSequence(forwardSchedule...),
			sched.NewSequence(backwardSchedule...),
		)

		// Loop through the forward and backward schedule until the delta stops
		// changing by more than loopMaxDelta
		loopSchedule = sched.NewLoop(combinedForwardBackwardSchedule, loopMaxDelta)
	}

	innerSchedule := sched.NewSequence(
		loopSchedule,
		sched.NewStep(sf.performanceToPerformanceDifferencFactors[0].UpdateMessage, 1),
		sched.NewStep(sf.performanceToPerformanceDifferencFactors[numPlayers-2].UpdateMessage, 2),
	)

	// Finally send the skill performances of all players
	posteriorSchedule := sched.NewSequence(skillFactorListToScheduleStep(sf.skillToPerformanceFactors, 1)...)

	// Combine all schedules into one runnable sequence
	fullSchedule := sched.NewSequence(priorSchedule, innerSchedule, posteriorSchedule)

	return fullSchedule
}