Search code examples
ralgorithmgenetic-algorithm

Soldiers guarding the city wall - homework assignment


I've got an assignment in my Artificial Intelligence class.

It is a problem which we have to solve using Genetic Algorithms in R (using GA library). I'm looking for some ideas how to approach this.

Description

Barbarians siege a city. General gives an order how many soldiers must guard a city wall on each hour during a day. This is his table:

Time of the day | Number of soldiers
00:00 - 01:00   150
01:00 - 02:00   160
02:00 - 03:00   160
03:00 - 04:00   170
04:00 - 05:00   350
05:00 - 06:00   380
06:00 - 07:00   400
07:00 - 08:00   420
08:00 - 09:00   450
09:00 - 10:00   470
10:00 - 11:00   500
11:00 - 12:00   500
12:00 - 13:00   450
13:00 - 14:00   350
14:00 - 15:00   300
15:00 - 16:00   300
16:00 - 17:00   310
17:00 - 18:00   350
18:00 - 19:00   350
19:00 - 20:00   330
20:00 - 21:00   300
21:00 - 22:00   250
22:00 - 23:00   200
23:00 - 24:00   170

Commander of defense wants the soldiers to be completely focused, when they are on patrol, so he gives this order:

  • Each soldier in guarding the wall for exactly 6 hours in a day (24 hours): 4 hours on the wall, then he rests for 2 hours and then he gets back on the wall for another 2 hours
  • The solders who start right before midnight, continue on the morning of the next day – algorithm must consider continuity of the days. How many soldiers are required to keep guarding the wall?

My ideas so far

Two of possible aproaches (so far):

First approach

Fitness function of GA would accept a number of solders and then perform a simulation:

  • Set initial score to 0.
  • If during the simulation there would be lack or excess of the soldiers in the city then subtract from score.
  • Optimal solution is when score is zero (number of required soldiers is optimal).

Second approach

Create a initial population (solders) and apply the rules from the commander in the fitness function (I don't really know how to define the above rules here) and then run GA until we get some useful score.


Solution

  • Since the assignment is over (yes, I go to the same class as OP) and this is an actually interesting problem, I'm posting my solution here:

    library(GA)
    
    Fitness <- function(x) {
        x <- as.integer(x)
        # z represents required soldiers at each hour
        z <- c(150,160,160,170,350,380,400,420,450,470,500,500,450,350,300,300,310,350,350,330,300,250,200,170)
        y <- rep(0,31)
        for (i in 1:length(x)) {
            y[i] <- y[i] + x[i]
            y[i+1] <- y[i+1] + x[i]
            y[i+2] <- y[i+2] + x[i]
            y[i+3] <- y[i+3] + x[i]
            #resting 4, 5
            y[i+6] <- y[i+6] + x[i]
            y[i+7] <- y[i+7] + x[i]
        }
        for (i in 1:7) {
            y[i] <- y[i]+y[24+i]
        }
        y <- y[1:24]
        p <- y >= z
        if (FALSE %in% p) { return(-3000) }
    return(-sum(x))
    }
    
    GA <- ga(type = "real-valued", fitness = Fitness, min = rep(0, 24), max = rep(150, 24), popSize = 24, suggestions = sol, maxiter = 5000, run = 5000, pmutation=0.9)
    
    summary(GA)
    sol <- (as.integer(GA@solution[1, ]))
    sol
    sum(sol)
    

    The GA generates a vector of numbers of soldiers that START their watch each hour (there is 24 hours, that's why 24 zeros and 24 times 150), and calls the Fitness function with said vector.

    The Fitness function first converts real numbers to integers (R as.integer function just strips off any number after decimal point, no rounding), and assigns the soldier to its 6 hours of work. It does that by adding the number of soldiers that start at x[i] to y[i, i+1, ... i+7] without those two hours when soldiers are resting.

    Because the soldiers guard around the clock, those last 7 hours in the y are added to the first 7 hours of y and then only first 24 values of y are considered.

    Then we compare the generated vector of soldiers on the watch to the required one, and if there are any FALSEs in the vector, Fitness returns a very big negative number (GA always takes bigger number as a better one).

    In the end we want to 'hire' as little soldiers as possible, that's why we negate the result (lower number of soldiers required = higher negated number) so the GA finds the best result.

    The rest is pretty self explanatory, we call the GA function, we fiddle with its settings and display the result.