title: Build a Mining Robot

text:
# Question
Title: Build a Mining Robot
Your program will control a mining robot searching underground for valuable minerals. Your robot will tell the controller where you want to move and dig, and the controller will provide feedback on your robot status.

Initially your robot will be given an image map of the mine with some mining shafts already present, and a data file specifying the value and hardness of the minerals in the mine. Your robot will then move through the shafts looking for valuable minerals to mine. Your robot can dig through the earth, but is slowed down by hard rock.

The robot that returns with the most valuable cargo after a 24 hour shift will be the winner. It may seem to be a complicated challenge but it is simple to make a basic mining robot (see the Sample Mining Robot answer below).

## Operation

Your program will be started by the controller with the mine image, mineral data, and equipment filenames. Robots can use the mine image and minerals data to find valuable ore and avoid hard rock. The robot may also want to buy equipment from the equipment list.

eg: `python driller.py mineimage.png minerals.txt equipmentlist.txt`

After a 2 second initialisation period, the controller communicates with the robot program through stdin and stdout. Robots must respond with an action within 0.1 seconds after receiving a status message.

Each turn, the controller sends the robot a status line:

`timeleft cargo battery cutter x y direction`

eg: `1087 4505 34.65 88.04 261 355 right`

The integer `timeleft` is the game seconds left before the end of shift. The `cargo` is the integer value of the minerals you have mined so far less what you have paid for equipment. The `battery` level is an integer percentage of your remaining battery charge. The `cutter` integer level is the current sharpness of the cutter as a percentage of the standard value. The `x` and `y` values are positive integers with the robot position referenced from the top left corner at (0, 0). The direction is the current direction the robot is facing (left, right, up, down).

When your robot receives the 'endshift' or 'failed' input, your program will soon be terminated. You might want your robot to write debugging/performance data to a file first.

There are 4 possible commands the controller will accept. `direction
left|right|up|down` will point your robot in that direction, and require 15 game-seconds. `move <integer>` will instruct your robot to move or dig that many units forward which takes time depending on the hardness of minerals cut and sharpness of your cutter (see below). `buy <equipment>` will install specified equipment and deduct the cost from your cargo value, but only if the robot is at the surface (y value \<= starting y value). Equipment installation takes 300 game-seconds. The special command `snapshot` writes the current mine image to disk and takes no game time. You can use snapshots to debug your robot or create animations.

Your robot will start with 100 battery and 100 cutter sharpness. Moving and turning use a small amount of battery power. Digging uses much more and is a function of the hardness of the minerals and current sharpness of the cutter. As your robot digs into minerals, the cutter will lose its sharpness, depending on the time taken and hardness of the minerals. If your robot has enough cargo value, it may return to the surface to buy a new battery or cutter. Note that high quality equipment has an initial effectiveness of over 100%. Batteries have the string "battery" in the name and (surprise) cutters have "cutter" in the name.

The following relationships define moving and cutting:

```
timecutting = sum(hardness of pixels cut) * 100 / cutter
cutterwear = 0.01 for each second cutting
cutters will not wear below 0.1 sharpness
timemoving = 1 + timecutting
batterydrain = 0.0178 for each second moving
changing direction takes 15 seconds and drains 0.2 from the battery
installing new equipment takes 300 seconds

```

Note that moving 1 unit without cutting any minerals takes 1 game second and uses 0.0178 of the battery. So the robot can drive 5600 units in 93 game minutes on a standard 100 charge, if it is not cutting minerals or turning.

NEW: The robot is 11 pixels wide so will cut up to 11 pixels with each pixel of movement. If there are less than 11 pixels to cut, the robot will take less time to move, and cause less wear on the cutter. If a pixel color is not specified in the mineral data file, it is free space of zero hardness and zero value.

The run is terminated when time runs out, the robot battery is exhausted, a part of the robot exceeds the image boundary, an illegal command is sent, or robot communication times out.

Your score is the final value of the robot cargo. The controller will output your score and the final map image. The stderr output of your program is logged in the robot.log file. If your robot dies, the fatal error may be in the log.

## The Mine Data

### equipment.txt:

```
Equipment_Name      Cost    Initial_Value
std_cutter          200     100
carbide_cutter      600     160
diamond_cutter      2000    250
forcehammer_cutter  7200    460
std_battery         200     100
advanced_battery    500     180
megapower_battery   1600    320
nuclear_battery     5200    570

```

### mineraldata.txt:

```
Mineral_Name        Color           Value   Hardness
sandstone           (157,91,46)     0       3
conglomerate        (180,104,102)   0       12
igneous             (108,1,17)      0       42
hard_rock           (219,219,219)   0       15
tough_rock          (146,146,146)   0       50
super_rock          (73,73,73)      0       140
gem_ore1            (0,255,0)       10      8
gem_ore2            (0,0,255)       30      14
gem_ore3            (255,0,255)     100     6
gem_ore4            (255,0,0)       500     21

```

### mine image:

The mine image may have an alpha channel, but this is not used.

## The Controller

The controller should work with Python 2.7 and requires the PIL library. I have been informed that the Python Pillow is a Windows friendly download to get the PIL image module.

Start the controller with the robot program, cfg.py, image and data files in the current directory. The suggested command line is:

`python controller.py [<interpreter>] {<switches>} <robotprogram>`

E.g.: `python controller.py java underminer.class`

The controller will write a robot.log file and a finalmine.png file at the end of the run.

```
#!/usr/bin/env python
# controller.py
# Control Program for the Robot Miner on PPCG.
# Tested on Python 2.7 on Ubuntu Linux. May need edits for other platforms.
# V1.0 First release.
# V1.1 Better error catching

import sys, subprocess, time
# Suggest installing Pillow here if you don't have PIL already
from PIL import Image, ImageDraw

from cfg import *

program = sys.argv[1:]
calltext = program + [MINEIMAGE, MINERALFILE, EQUIPMENTFILE]
errorlog = open(ERRORFILE, 'wb')
process = subprocess.Popen(calltext,
            stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=errorlog)

image = Image.open(MINEIMAGE)
draw = ImageDraw.Draw(image)
BLACK, ORANGE, WHITE = (0,0,0), (255,160,160), (255,255,255)
W,H = image.size
dirmap = dict(right=(1,0), left=(-1,0), up=(0,-1), down=(0,1))

# read in mineral file (Name, Color, Value, Hardness):
data = [v.split() for v in open(MINERALFILE)][1:]
mineralvalue = dict((eval(color), int(value)) for
    name, color, value, hard in data)
hardness = dict((eval(color), int(hard)) for
    name, color, value, hard in data)

# read in the equipment list:
data = [v.split() for v in open(EQUIPMENTFILE)][1:]
equipment = dict((name, (int(cost), float(init))) for
    name, cost, init in data)

# Set up simulation variables:
status = 'OK'
rx, ry, direction = START_X, START_Y, START_DIR    # center of robot
cargo, battery, cutter = 0, 100.0, 100.0
clock = ENDSHIFT
size = ROBOTSIZE / 2
msgfmt = '%u %u %u %u %u %u %s'
snapnum = 1

def mkcutlist(x, y, direc, size):
    dx, dy = dirmap[direc]
    cx, cy = x+dx*(size+1), y+dy*(size+1)
    output = [(cx, cy)]
    for s in range(1, size+1):
        output += [ (cx+dy*s, cy+dx*s), (cx-dy*s, cy-dx*s)]
    return output

def send(msg):
    process.stdin.write((msg+'\n').encode('utf-8'))
    process.stdin.flush()

def read():
    return process.stdout.readline().decode('utf-8')

time.sleep(INITTIME)
while clock > 0:
    try:
        start = time.time()
        send(msgfmt % (clock, cargo, battery, cutter, rx, ry, direction))
        inline = read()
        if time.time() - start > TIMELIMIT:
            status = 'Move timeout'
            break
    except:
        status = 'Robot comslink failed'
        break

    # Process command:
    movecount = 0
    try:
        arg = inline.split()
        cmd = arg.pop(0)
        if cmd == 'buy':
            if ry <= START_Y and arg and arg[0] in equipment:
                cost, initperc = equipment[arg[0]]
                if cost <= cargo:
                    cargo -= cost
                    if 'battery' in arg[0]:
                        battery = initperc
                    elif 'cutter' in arg[0]:
                        cutter = initperc
                    clock -= 300
        elif cmd == 'direction':
            if arg and arg[0] in dirmap:
                direction = arg[0]
                clock -= 15
                battery -= 0.2
        elif cmd == 'move':
            if arg and arg[0].isdigit():
                movecount = abs(int(arg[0]))
        elif cmd == 'snapshot':
            image.save('snap%04u.png' % snapnum)
            snapnum += 1
    except:
        status = 'Robot command malfunction'
        break

    for move in range(movecount):
        # check image boundaries
        dx, dy = dirmap[direction]
        rx2, ry2 = rx + dx, ry + dy
        print rx2, ry2
        if rx2-size < 0 or rx2+size >= W or ry2-size < 0 or ry2+size >= H:
            status = 'Bounds exceeded'
            break
        # compute time to move/cut through 1 pixel
        try:
            cutlist = mkcutlist(rx2, ry2, direction, size)
            colors = [image.getpixel(pos)[:3] for pos in cutlist]
        except IndexError:
            status = 'Mining outside of bounds'
            break
        work = sum(hardness.get(c, 0) for c in colors)
        timetaken = work * 100 / cutter
        cutter = max(0.1, cutter - timetaken / 100)
        clock -= 1 + int(timetaken + 0.5)
        battery -= (1 + timetaken) / 56
        if battery <= 0:
            status = 'Battery exhausted'
            break
        cargo += sum(mineralvalue.get(c, 0) for c in colors)
        draw.rectangle([rx-size, ry-size, rx+size+1, ry+size+1], BLACK, BLACK)
        rx, ry = rx2, ry2
        draw.rectangle([rx-size, ry-size, rx+size+1, ry+size+1], ORANGE, WHITE)
        if clock <= 0:
            break

    if status != 'OK':
        break

del draw
image.save('finalmine.png')
if status in ('Battery exhausted', 'OK'):
    print 'Score = %s' % cargo
    send('endshift')
else:
    print 'Error: %s at clock %s' % (status, clock)
    send('failed')

time.sleep(0.3)
process.terminate()

```

The linked config file (not to be changed):

```
# This is cfg.py

# Scenario files:
MINEIMAGE = 'testmine.png'
MINERALFILE = 'mineraldata.txt'
EQUIPMENTFILE = 'equipment.txt'

# Mining Robot parameters:
START_X = 270
START_Y = 28
START_DIR = 'down'
ROBOTSIZE = 11      # should be an odd number

ENDSHIFT = 24 * 60 * 60   # seconds in an 24 hour shift

INITTIME = 2.0
TIMELIMIT = 0.1

ERRORFILE = 'robot.log'

```

## Answer Format

The answers should have a title including programming language, robot name, and final score (such as *Python 3*, *Tunnel Terror*, *1352*). The answer body should have your code and the final mine map image. Other images or animations are welcome too. The winner will be the robot with the best score.

## Other Rules

* The common loopholes are prohibited.
* If you use a random number generator, you must hardcode a seed in your program, so that your program run is reproducable. Someone else must be able to run your program and get the same final mine image and score.
* Your program must be programmed for *any* mine image. You must not code your program for these data files or *this* image size, mineral layout, tunnel layout, etc. If I suspect a robot is breaking this rule, I reserve the right to change the mine image and/or data files.

## Edits

* Explained 0.1 second response rule.
* Expanded on robot starting command line options and files.
* Added new controller version with better error catching.
* Added robot.log note.
* Explained default mineral hardness and value.
* Explained battery vs cutter equipment.
* Made robot size 11 explicit.
* Added calculations for time, cutter wear, and battery.

# Answer
> 3 votes
# Python 2, Sample Miner, 350

This is an example of the minimum code for a mining robot. It just digs straight down until its battery gives out (all robots start pointing down). It only earns a score of 350. Remember to flush stdout or else the controller will hang.

```
import sys
# Robots are started with 3 arguments:
mineimage, mineralfile, equipmentfile = sys.argv[1:4]
raw_input()           # ignore first status report
print 'move 1000'     # dig down until battery dies
sys.stdout.flush()    # remember to flush stdout
raw_input()           # wait for end message

```

# Answer
> 2 votes
# Python 2, Robot Miner Python Template, 410

This is a mining robot template to show how a robot operates and provide a framework for building your own robots. There is a section for analyzing the mineral data, and a section for responding with actions. The placeholder algorithms do not do well. The robot finds some valuable minerals, but not enough to buy enough replacement batteries and cutters. It halts with a dead battery on its way to the surface to a second time.

A better plan is to use the existing tunnels to get close to valuable minerals and minimise the digging.

Note that this robot writes a log file of each status message it receives so you can check it's decisions after a run.

```
import sys
from PIL import Image

MINEIMAGE, MINERALFILE, EQUIPMENTFILE = sys.argv[1:4]
image = Image.open(MINEIMAGE)
W,H = image.size
robotwidth = 11
halfwidth = robotwidth / 2

# read in mineral file (Name, Color, Value, Hardness):
data = [v.split() for v in open(MINERALFILE)][1:]
mineralvalue = dict((eval(color), int(value)) for
    name, color, value, hard in data)
hardness = dict((eval(color), int(hard)) for
    name, color, value, hard in data)

# read in the equipment list:
data = [v.split() for v in open(EQUIPMENTFILE)][1:]
equipment = [(name, int(cost), float(init)) for
    name, cost, init in data]
# Find the cheapest battery and cutter for later purchase:
minbatcost, minbatname = min([(c,n) for
    n,c,v in equipment if n.endswith('battery')])
mincutcost, mincutname = min([(c,n) for
    n,c,v in equipment if n.endswith('cutter')])

# process the mine image to find good places to mine:
goodspots = [0] * W
for ix in range(W):
    for iy in range(H):
        color = image.getpixel((ix, iy))[:3]   # keep RGB, lose Alpha
        value = mineralvalue.get(color, 0)
        hard = hardness.get(color, 0)
        #
        # -------------------------------------------------------------
        # make a map or list of good areas to mine here
        if iy < H/4:
            goodspots[ix] += value - hard/10.0
        # (you will need a better idea than this)
goodshafts = [sum(goodspots[i-halfwidth : i+halfwidth+1]) for i in range(W)]
goodshafts[:halfwidth] = [-1000]*halfwidth   # stop robot going outside bounds
goodshafts[-halfwidth:] = [-1000]*halfwidth
bestspot = goodshafts.index(max(goodshafts))
# -----------------------------------------------------------------
#

dirmap = dict(right=(1,0), left=(-1,0), up=(0,-1), down=(0,1))
logging = open('mylog.txt', 'wt')
logfmt = '%7s %7s %7s %7s %7s %7s %7s\n'
logging.write(logfmt % tuple('Seconds Cargo Battery Cutter x y Direc'.split()))
surface = None
plan = []

while True:
    status = raw_input().split()
    if status[0] in ('endshift', 'failed'):
        # robot will be terminated soon
        logging.close()
        continue
    logging.write(logfmt % tuple(status))
    direction = status.pop(-1)
    clock, cargo, battery, cutter, rx, ry = map(int, status)
    if surface == None:
        surface = ry    # return to this level to buy equipment
    #
    # -----------------------------------------------------------------
    # Decide here to choose direction, move, buy, or snapshot
    if not plan and rx != bestspot:
        plan.append('direction right' if bestspot > rx else 'direction left')
        plan.append('move %u' % abs(bestspot - rx))
        plan.append('direction down')

    if plan:
        action = plan.pop(0)
    elif battery < 20 and cargo > minbatcost + mincutcost:
        action = 'direction up'
        move = 'move %u' % (ry - surface)
        buybat = 'buy %s' % minbatname
        buycut = 'buy %s' % mincutname
        plan = [move, buybat, buycut, 'direction down', move]
    else:
        action = 'move 1'
    # -----------------------------------------------------------------
    #
    print action
    sys.stdout.flush()

```

---
Tags: code-challenge, image-processing, king-of-the-hill, path-finding
---


title: Predict the Landslide

text:
# Question
Title: Predict the Landslide
# Landslides

In this challenge, your job is to predict the extent of damage caused by a massive landslide. We use the following simplified two-dimensional model for it, parameterized by an *initial height* `h >= 0` and a *critical coefficient* `c > 0`. You start with a cliff of height `h`, and it is assumed that the terrain is completely flat infinitely to the left and to the right of it. For `h = 6`, the situation looks like this:

```
##########
##########
##########
##########
##########
##########
-----------------------

```

The `-` are immovable bedrock, and the `#` are unstable soil. If the height difference between two neighboring columns is more than `c`, a *landslide* occurs: the top `c` units of soil from the left column fall down to the next `c` columns on the right, one to each. The rightmost non-empty column in the figure is unstable for `c = 2`, so a landslide is triggered:

```
#########
#########
##########
##########
##########
############
-----------------------

```

The column is still unstable, which causes a second landslide:

```
#########
#########
#########
#########
############
############
-----------------------

```

Now, the column on its left has become unstable, so a new landslide is triggered there:

```
########
########
#########
###########
############
############
-----------------------

```

After this, the cliff is stable again. The nice thing about this model is that the order in which the landslides are processed does not matter: the end result is the same.

# The Task

Your program is given the integer parameters `h` and `c` as inputs (the order does not matter, but you must specify it on your answer), and it should output the total number of *columns* that the landslide affects. This means the number of columns in the resulting stable cliff whose height is strictly between `0` and `h`. In the above example, the correct output is `4`.

You can write a full program or a function. The lowest byte count wins, and standard loopholes are disallowed.

# Test Cases

These are given in the format `h c -> output`.

```
0  2  -> 0
2  3  -> 0
6  2  -> 4
6  6  -> 0
10 1  -> 10
15 1  -> 14
15 2  -> 11
15 3  -> 6
40 5  -> 16
80 5  -> 28
80 10 -> 17

```

# Answer
# CJam, ~~62~~ 57 bytes

As far as I can see, this is completely different approach to implement the solution from aditsu's answer.

```
q~:C;:HaH)*H){(:I\_@>2<:-C>{I0a*C~)+C1a*+]z1fb_,}I?}h-H-,

```

Input goes in the form of `h c`

Example:

```
80 5

```

Output:

```
28

```

**How it works**

The logic is pretty straight forward here with a few tricks used to reduce code size.

* Get `h + 1` (`+ 1` for `h = 0` case) length array with each element being `h` to represent the cliff
* Start iterating from the right most index of this array
    + If the two elements from current index have different more than `c`
        - Remove `c` from the current index element
        - Add `1` to next `c` elements of the array from the current index
        - Make the current index equal to the length of this new array
        - This makes sure that we stabilize the stones to the right of the current index first
    + else, reduce the current index
* When we hit the left most index, we are making sure that all adjacent indexes have less than or equal to `c` difference
* Remove any `0` or `h` value from the array and get length.

**Code expansion**

```
q~:C;:HaH)*H){(:I\_@>2<:-C>{I0a*C~)+C1a*+]z1fb_,}I?}h-H-,
q~:C;:HaH)*H)
q~:C;:H                  "Read the input, evaluate it, store height in H and coeff. in C";
       aH)*              "Wrap the height number in an array and repeat it H + 1 times";
           H)            "Put H+1 on stack, representing the current index of iteration";
{(:I\_@>2<:-C>{I0a*C~)+C1a*+]z1fb_,}I?}h
(:I\_@>2<:-C>
(:I                      "Decrement the current index and store it in I";
   \_                    "Swap to put array on top and make 1 copy";
     @>2<                "Get the two elements starting from Ith index";
         :-              "Get the difference. The best part of this approach is that";
                         "For the right most index, where there is only element, it";
                         "returns the element itself, which is the expected difference";
           C>            "Check if difference is greater than C";
{I0a*C~)+C1a*+]z1fb_,}   "This block will be executed when the difference is more than C";
 I0a*                    "Get an array of I length and all elements 0";
     C~)+                "Get -C value and append it to the above array";
         C1a*+           "Get C length array of 1s and concat with the above array";
              ]          "Wrap the two arrays, the cliff and the above one in an array";
               z1fb      "Transpose to get number pairs and add those pairs. For example";
                         "If we are at the right most index with H = 80 and C = 5,";
                         "The right section of the cliff looks like:";
                         "[ ... 80 80 80 80 80] and the array created in above step";
                         "looks like [ ... 0 0 0 0 -5 1 1 1 1 1]. After z, we have:";
                         "[ ... [80 0] [80 0] [80 0] [80 0] [80 -5] [1] [1] [1] [1] [1]]";
                         "After 1fb we get [ ... 80 80 80 80 75 1 1 1 1 1]";
                   _,    "Take a copy of the above resultant array and take its length";
I?                       "If difference was not greater than C, put I on stack";
                         "Now we either have the decremented index or new array length";
                         "on stack."
{ ... }h                 "This is a do while loop which makes sure that we iterate to";
                         "the left of the array. This loops runs till the top stack";
                         "element is 0 while not popping the top element";
        -H-,             "After the loop, we have the final cliff array and 0 on stack";
                         "Remove any 0 elements from the array, then remove any H";
                         "elements from the array and then take length to get the";
                         "number of columns which were modified";

```

Try it online here
> 5 votes

# Answer
# CJam - 70

```
q~:C;:H0]H*$W%{[__W<\1>]z{~-}%{C>}#):I{I(_2$=C-tC,{I+_2$=)t}/}0?}h-H-,

```

Try it at http://cjam.aditsu.net/

**Explanation:**

```
q~                    read and evaluate the input
:C;                   store the 2nd number in C and remove
:H                    store the first number in H
0]H*                  make an array [H 0] and repeat it H times
$W%                   sort and reverse, obtaining [(H H's) (H 0's)] (initial cliff)
{                     loop...
    [__W<\1>]         make an array with the cliff without the first column
                      and the cliff without the last column
    z{~-}%            subtract the 2 arrays to get the height differences
    {C>}#             find the index of the first height diff. greater than C
    ):I               increment and store in I
    {                 if the value is non-zero (i.e. landslide occurring)
        I(_2$=C-t     subtract C from the corresponding column height
        C,            make an array [0 1 ... C-1]
        {             for each of those numbers
            I+        add I, obtaining a column index where some soil falls
            _2$=)t    increment the column height
        }/            end loop
    }0?               else break outer loop; end if
}h                    ...while the condition is true
-H-                   remove all 0 and H from the final stable cliff
,                     count the remaining columns

```

The `h` operator checks the last value on the stack without removing it. If a landslide occurred, the value is the cliff array, which evaluates to true because it's not empty. If not, the last value is 0 (false).
So in case of landslide, the loop continues with the array on the stack, otherwise it ends with a 0 pushed after the array. That 0 is then removed from the array by the next `-` operator.
> 4 votes

# Answer
# Python, ~~200~~ ~~190~~ 174

```
h,c=input();q=[h]*h+[0]*h
try:
 while 1:
    d=[b-a for a,b in zip(q[1:],q)];g=max(d);a=d.index(g)
    for i in range(c):q[a+1+i]+=1/(g>c);q[a]-=1
except:print sum(h>i>0for i in q)

```

Expanded version:

```
h, c = input()
# Initialize the heights
q = [h]*h + [0]*h
try:
    while 1:
        # Difference between the heights
        d = [b-a for a,b in zip(q[1:],q)]
        # It may error here, when h == 0, but thats okay
        g = max(d)
        a = d.index(g)
        for i in range(c):
            # This is the termination condition, when g <= c
            q[a+1+i] += 1 / (g>c)
            # Save the newline; also move this line to after termination
            q[a] -= 1
except:
    # Count all heights that have changed
    print sum(h > i > 0 for i in q)

```

**Edit:** After some optimizing, I eliminated the awkward loop termination via break (saves 1 byte). Also changed the slide from slice based to loop based.
> 4 votes

# Answer
# Python 2 - ~~194~~ 158 bytes

```
h,c=input()
b=l=[h]*h+[0]*h
while b:
 b=0
 for i in range(len(l)-1):
  if l[i]-l[i+1]>c:
    for j in range(c):l[i-~j]+=1
    l[i]-=c;b=1
print sum(h>e>0for e in l)

```

(Note that SE's markdown interpreter converts literal tabs to 4 spaces. Lines 7 and 8 of this program have only a single tab \[i.e. one byte\] of indentation each.)

Takes input on stdin, `h` first. For example:

```
$ ./landslide.py <<< '6, 2'
4

```

This program has been through a lot of improvements. I had been editing this answer to explain some of the more major edits, but it was getting kind of long. You can check the edit history if you're curious.

**Explanation**

First, `h`and `c` are read from stdin. In Python 2, `input()` is equivalent to `eval(raw_input())`, which is why I ask for a comma separating the numbers. `input()` gives returns a tuple of ints, no conversion required.

Next, a list of integers is made. It is `2*h` long. The first half are `h` and the second half are 0. I don't have any reasoning to show that this is enough to simulate infinite `h`s to the left and 0s to the right. I just sort of stumbled into it and it works for all the test cases, so if someone can find input it doesn't work for I'll gladly change it. Anyway, this list is called `l`, but another copy of it is called `b`.

`b`'s value doesn't actually matter, all that matters is that it's truthy. A non-empty list is truthy and the only way `b` can be empty here is if `h` is 0, in which case the correct answer is still printed. In any other case, `b` has to be truthy to ensure we enter the `while b:` loop. However, the first thing that happens in the loop is setting `b` to 0, a falsey value. During each repetition of the loop `b` must be specifically set back to a truthy one or the loop will end.

The rest of the loop is the actual simulation. It's very naive, essentially just being a code translation of the problem description. If any element of `l` is more than `c` greater than the one following it, it is subtracted by `c` and the next `c` elements have 1 added to them. (The bitwise magic used here is just a shorter way of writing `i+1+j`, by the way.) While making these transformations, `b` is set to 1. The first time no transformations are made, `b` will stay 0 and the loop terminates.

Any true expression evaluates to `True`, and when you try to do math on `True` it evaluates to 1. The same is true of `False` and 0. The last line of the program uses every element of `l` as `e` in the expression `h>e>0` and sums the result. This gets the number of columns greater than 0 but lower than the original cliff height, which is the value the question asks for. It's printed and the program exits.
> 4 votes

# Answer
# Haskell, 163 156 151 Bytes

```
h#c=sum[1|e<-(until=<<((==)=<<))s$r h++r 0,e`mod`h/=0]where r=replicate$h+1;s w@(x:y:z)|x==0=w|x>c+y=x-c:map(+1)(take c(y:z))++drop c(y:z)|1<2=x:s(y:z)

```

Usage: `h#c`, e.g. `6#2` which outputs `4`.

How it works: the helper function `s` does a single landslide. Repeatedly apply `s` until the output does not change anymore. Count the affected elements.

Found the "apply until output doesn't change" function (i.e. `until=<<((==)=<<)`) at Stackoverflow.
> 3 votes

# Answer
# Mathematica, ~~108~~ ~~104~~ ~~100~~ ~~97~~ 95

```
f=Max@#-Min@#&[0Range@#2//.{x___,k:##..&[a_,{#}],a_,y___}:>Sort@{x,##&@@({k}-1),a+#,y}/.{}->0]&

```

Usage:

```
f[c, h]

```

Example:

```
f[5, 80]

```

> 28
> 3 votes

# Answer
### C# 303 295

It works!

But it is a ....

```
int q(int n,int c){var s=Enumerable.Repeat(n,n).ToList();s.Add(0);var d=new HashSet<int>();var g=true;while(g){g=false;for(int i=s.Count-1;i>0;i--){int z=i;int y=i-1;if((s[y]-s[z])>c){s[y]-=c;d.Add(y);g=true;for(int j=1;j<=c;j++){s[y+j]++;d.Add(y+j);if(s[s.Count-1]>0)s.Add(0);}break;}}}return d.Count;}

```

I must find new language ;)

I'll check this CJam thing ...

Improved:

```
int q(int n,int c){var s=Enumerable.Repeat(n,n).ToList();s.Add(0);var d=new HashSet<int>();var g=1>0;while(g){g=1<0;for(int i=s.Count-1;i>0;i--){int z=i,y=i-1;if((s[y]-s[z])>c){s[y]-=c;d.Add(y);g=1>0;for(int j=1;j<=c;j++){s[y+j]++;d.Add(y+j);if(s[s.Count-1]>0)s.Add(0);}break;}}}return d.Count;}

```
> 2 votes

---
Tags: code-golf
---