title: (Re)Implementing Tetris

text:
# Question
Title: (Re)Implementing Tetris
In the spirit of re-implementing classic video games, I would like to invite the community to create their best implementation of Tetris.

*For reference, a screenshot of the official NES version of Tetris.*

## Required Features

* A reasonable scoring system must be in place, which rewards multi-line clears more than single-line clears. The current score must be visible at all times.
* The next piece that will appear must be indicated in some manner.
* The distribution of the seven tetrominoes should be fairly even (i.e. pseudo-randomly chosen).
* The user must have the ability to rotate the current piece in both directions, as well as accelerate its descent.
* When the game has ended, it should be clearly indicated that the game is over.
* The source code must be structured and easily understandable.

## Optional Features

* Advancing falling speed after a certain number of clears (i.e. increasing difficulty level), and advancing score per line clear, proportional to speed.
* Gravity. You may choose to implement the 'classic' gravity, in which blocks can remain floating over gaps, or you may choose to implement 'flood fill' gravity, in which blocks which have been separated from their original tetromino via line clears may fall into open gaps.
* High scores with name input.
* Animation after line clears, and/or after obtaining a new high score.

## Limitations

* Any libraries used (jQuery, PyGame, etc.) should be freely available.
* Source code size must not exceed 4096 bytes, excluding whitespace and comments. Any external resources (data files, images, etc.) will be added to the code length, excluding any files which are generated, such as for high scores.
*I realize that this is a rather arbitrary restriction; my primary goal is to discourage copy-pasting of existing implementations, and to encourage brevity and self-containment.*

## Winning Criteria

This challenge will be judged as a **popularity contest**, meaning that the submission with the most upvotes will be selected as the winner. When upvoting, I encourage users to upvote any and all submissions which they feel adequately meet the above stated requirements.

The winner will be chosen no sooner than 2 weeks after the first valid solution. Additionally, I will be granting a bounty to the winner, roughly proportional to the number of upvotes this question receives (`10 * #votes` rounded upwards to the nearest 50). Should there be a tie after the 2 week period has expired, the competition period will be extended by one week. Should there still be a tie, I reserve the right to place the final vote.

Please ask for any clarifications. May the best implementation win!

# Answer
> 19 votes
## Try: http://tetris.muehe.org

***Update*** There's a global high score. Enjoy beating it or - alternatively - hacking it :-)

CoffeeScript and HTML version, should fulfill the requirements to the best of my knowledge (and I have never really played Tetris).

```
$ make stats
4095

```

Github https://github.com/henrik-muehe/tetris

## Features

* Exponential scoring
* Next piece indicator
* Fair randomness
* Rotation, move and drop down
* Game over indication
* **Highscore** with server backend that should not be *that* easily fakeable.

# Answer
> 19 votes
# Pascal

Developed in FreePascal 2.6.2, should compile with Turbo Pascal 6.0 too. Only the Crt unit is used, no external resources.

```
program tetris;

  uses
    Crt;

  const
    width = 10;                                  { playing field width }
    height = 20;                                 { playing field height }

  const
    piece: array [1..7, 1..3, 0..1] of ShortInt = ( { piece shapes : piece, element, coordinate }
      (( 1, 0), ( 1, 1), ( 0, 1)),               { O }
      ((-1, 0), ( 1, 0), ( 2, 0)),               { I }
      (( 0, 1), ( 1, 0), ( 2, 0)),               { L }
      (( 0, 1), (-1, 0), (-2, 0)),               { J }
      ((-1, 0), ( 1, 0), ( 0, 1)),               { T }
      (( 1, 0), ( 0, 1), (-1, 1)),               { S }
      ((-1, 0), ( 0, 1), ( 1, 1))                { Z }
    );
    color: array [1..7, 0..1] of Byte = (        { piece colors : foreground, background }
      (Yellow,       Brown),                     { O }
      (LightCyan,    Cyan),                      { I }
      (LightBlue,    Blue),                      { L }
      (White,        LightGray),                 { J }
      (LightMagenta, Magenta),                   { T }
      (LightGreen,   Green),                     { S }
      (LightRed,     Red)                        { Z }
    );

  var
    area: array [1..width + 2, 1..height + 1] of Byte; { playing field }
    coord: array [0..3, 0..1] of Byte;           { precalculated element coordinates }
    played,                                      { played pieces count }
    removed,                                     { completed lines count }
    level,                                       { current level }
    score: LongInt;                              { accumulated score }
    time,                                        { current level delay }
    wait,                                        { current piece delay }
    made,                                        { completed lines in current level }
    multi,                                       { multiline count }
    screen,                                      { original screen size }
    i, j, j2: Word;                              { counters }
    current,                                     { current piece }
    next,                                        { next piece }
    x,                                           { horizontal coordinate }
    y,                                           { vertical coordinate }
    position,                                    { rotation position }
    k: Byte;                                     { pressed key }
    ok: Boolean;                                 { aggregated condition }

{ precalculates the give piece's elements coordinates }
  procedure coordinate(current, x, y, position: Byte);
  begin
    coord[0, 0] := x;
    coord[0, 1] := y;
    for i := 1 to 3 do begin
      coord[i, 0] := x + piece[current, i, position mod 2] * (Ord(position in [0, 1]) * 2 - 1);
      coord[i, 1] := y + piece[current, i, 1 - position mod 2] * (Ord(position in [0, 3]) * 2 - 1);
    end;
  end;

{ draws a piece }
  procedure draw(current, x, y, position: Byte; visible: Boolean);
  begin
    coordinate(current, x, y, position);

    for i := 0 to 3 do begin
      GotoXY(coord[i, 0] * 2 + 1, coord[i, 1]);
      if visible then begin
        TextColor(color[current, 0]);
        TextBackground(color[current, 1]);
        Write('[]');
      end else begin
        TextBackground(Black);
        Write('  ');
      end;
    end;
  end;

{ check whether a piece can be placed in given position }
  function check(x2, y2, position2: Byte): Boolean;
  begin
    coordinate(current, x2, y2, position2);

    ok := True;
    for i := 0 to 3 do if area[coord[i, 0], coord[i, 1]] <> 0 then ok := False;

    if ok then begin
      x := x2;
      y := y2;
      position := position2;
    end;

    check := ok;
  end;

begin

  Randomize;
  TextColor(LightGray);
  TextBackground(Black);
  ClrScr;
  screen := WindMax;

  for i := 0 to width + 1 do for j := 1 to height + 1 do area[i, j] := 9 * Ord(not ((i in [1..width]) and (j in [1..height])));

  Window(1, 1, width * 2 + 4, height + 2);
  for i := 1 to (width + 2) * (height + 1) do Write('##');
  Window(3, 1, width * 2 + 2, height);
  ClrScr;
  Window(1, 1, Lo(screen), Hi(screen));

  Window(width * 2 + 7, 1, width * 2 + 25, 10);
  Writeln('Next');
  Writeln;
  Writeln;
  Writeln('Piece');
  Writeln('Line');
  Writeln('Level');
  Writeln('Score');
  Window(1, 1, Lo(screen), Hi(screen));

  played := 0;
  removed := 0;
  level := 1;
  score := 0;

  current := 9;
  next := 9;
  made := 0;

  repeat

    if current = 9 then begin
      if next = 9 then next := Random(7) + 1 else draw(next, width + 9, 1, 0, False);

      current := next;
      next := Random(7) + 1;
      x := width div 2;
      y := 1;
      position := 0;
      time := 1100 - level * 100;
      wait := time;

      Inc(played);
      Inc(score);
      if made = 25 then begin
        Inc(level);
        made := 0;
      end;

      draw(next, width + 9, 1, 0, True);

      Window(width * 2 + 15, 4, width * 2 + 25, 10);
      TextColor(LightGray);
      TextBackground(Black);
      Writeln(played:5);
      Writeln(removed:5);
      Writeln(level:5);
      Writeln(score:5);
      Window(1, 1, Lo(screen), Hi(screen));

      if not check(x, y, position) then begin
        GotoXY(width * 2 + 7, 10);
        Write('Game Over');
        Break;
      end;
    end;

    draw(current, x, y, position, True);
    GotoXY(1, 1);
    repeat Delay(1);
      Dec(wait);
    until KeyPressed or (wait = 0);
    draw(current, x, y, position, False);

    if KeyPressed then begin
      k := Ord(ReadKey);
      case k of
        75, 77: check(x + Ord(k = 77) * 2 - 1, y, position);
        72, 80: check(x, y, (position + Ord(k = 80) * 2 + 1) mod 4);
        32: begin
          time := 1;
          Inc(score);
        end;
      end;
    end;

    if wait = 0 then begin
      if not check(x, y + 1, position) then begin
        draw(current, x, y, position, True);
        for i := 0 to 3 do area[coord[i, 0], coord[i, 1]] := current;

        multi := 0;
        for j := 1 to height do begin
          ok := True;
          for i := 1 to width do if area[i, j] = 0 then ok := False;

          if ok then begin
            for j2 := j downto 2 do for i := 1 to width do area[i, j2] := area[i, j2 - 1];
            for i := 1 to width do area[i, 1] := 0;

            Inc(score, 10 + multi * 2);
            Inc(removed);
            Inc(multi);

            Window(3, 1, width * 2 + 2, height);
            TextBackground(Black);
            GotoXY(1, j);
            DelLine;
            GotoXY(1, 1);
            InsLine;
            Window(1, 1, Lo(screen), Hi(screen));
          end;

        end;
        if multi <> 0 then Inc(made);

        current := 9;
      end;
      wait := time;
    end;

  until k = 27;

  ReadKey;
  TextColor(LightGray);
  TextBackground(Black);
  ClrScr;

end.

```

### Screenshot

(On Linux, in XTerm window.)

### Control

* `Left` – move left
* `Right` – move right
* `Up` – rotate counterclockwise
* `Down` – rotate clockwise
* `Space` – drop down
* `Esc` – exit

### Scoring

* played piece – 1 point
* dropped piece – 1 point
* completed line – 10 point
* multiple lines – multiplier * 2 point

Level starts at 1 and increases after each 25 line completing. (Multiple lines completed at once count as 1.)

### Measurement

```
bash-4.2$ sed '
s/{[^{}]*}//g                  # remove comments
s/^ *\| *$//g                  # trim leading and trailing spaces
/^$/d                          # remove empty lines
s/ *\([:=<>,;+*-]\+\) */\1/g   # no space around operators
' tetris.pas | wc -c
3697

```

# Answer
> 18 votes
## Java (Swing)

This is an implementation of the first historical Game Boy edition from **Nintendo(c)** from 1989.

How to play:

`Z` = rotate left
`X` = rotate right
`Left` = move left
`Right` = move right
`Down` = move down (slowly)
`Up` = rotate left (just for easier use)
`R` = reset game

I avoided to use more than one class (because the golf aspect comes to my mind). But now it's not golfable anymore in any case... However, I zipped and Base64-encoded one Font-File and one Image-File, so I can use it in the one and only class file.

To run it, copy the Java code into your IDE and start. You don't need any additional libraries or resources.

```
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.zip.ZipInputStream;

import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import javax.xml.bind.DatatypeConverter;

public class JTetris extends javax.swing.JFrame {
    static Image IMAGE;
    static Font FONT;

    static {
        ZipInputStream zipImage = new ZipInputStream(new ByteArrayInputStream(DatatypeConverter.parseBase64Binary(
            "UEsDBBQAAAAIABmiWkRZ0oOouwAAAJYKAAAFAAAAYy5ibXBz8p3GxQAGZUCsAcQ3gFgCiBkZWMDiCkB5GBCA0iJyLAwl+3oYYpJsGK58u8NAKjBGAgyUgVG7QHYxKUGBAjDiBJUUQUhQAMQGA3xsFL3MMBMNRrpdEABhQxQLCiDYmHZBxCF2QdhAu8CyEHLk2cWkBJSH" +
            "xxcoCgyg5hsbAgWhbBgJUg9Wg24XWDGQHJF2QeOCUruQwEi0S0kRe5qHKCY2zQsgkv0ItAsIKC97EQnecGTYRQUwKNsbNAYAUEsBAj8AFAAAAAgAGaJaRFnSg6i7AAAAlgoAAAUAJAAAAAAAAAAgAAAAAAAAAGMuYm1wCgAgAAAAAAABABgAkJCiSyczzwGQkKJLJzPP" +
            "AZCQoksnM88BUEsFBgAAAAABAAEAVwAAAN4AAAAAAA=="
        )));

        ZipInputStream zipFont = new ZipInputStream(new ByteArrayInputStream(DatatypeConverter.parseBase64Binary(
            "UEsDBBQAAAAIAEq5UEN0MGyWoggAAHgrAAAFAAAAYS50dGbklsvvC1EUx78zU+/3OxHiEuLd1jMlxKNR70cQCxumY9oOnU5Np6osdIGEDYmwZGElkYh/gI2FBUIsvFdILNgh8Rxft4d6RyxEYvq7PZ97es73nHNv8mthAOiJFiyotRtS0xMnum4Gej0HsNXx7erbI6+7" +
            "ks8CxqNiuVk48qTbVKB3BIy8XXLtbVv6x0cBkwuzS3SYrffvuL8MYGzJj3av3BfP4P4p8yeUA8cGxqWBSau4n+Lbu6tYET8AEoMBqIrtuzuPz33D/Uyg28VqUItgxtTrvRGAAoW4LK7BsHTnb5HAE9qB6E+PidGYhCTmYjP24AxuxTGYR98UpLEIdtsXP4zvxXfjO/HN" +
            "+Gp8Kg6p9O8//8ucRns93ZK6vqXfvBccXLuv7b/QH2LjVtyyYLUAdIfJaMmz6Gc83w3yd9OuSLS0WmcxRLj1xYJY/Gz9Wcyfr79XJ6aO3MEKTEAPWsDE988Y4yQM4RGAsEEeIWyiGzLCFsZjgXACY1EQ7oJeOCzclZnHhbthHs4Ld8cQPBPugT6GKdwT841+wr0wzFgl" +
            "3BtpwxHugxHGMeG+mGycEx6EicbdNhusYFqwYCQ4LTJmL2GDnBU20dfcK2xhjXlIOIFl5i3hLhhqjRLuiow1S7gbdlu+cHdMsh4L98DwRD/hntiTmCjcC8nEaeHe2Jq4IdwHmS7ThPtiUxdPeBDWd7nYZgPo33VANqg2Q69YitQ6Owo9Z4daZdd3uGp6etqMDfWqG6rV" +
            "dugF9Fa2qQlL+T9XLQ6aE9V6t1gv22EuqEQborDuROoX0b/87JPUJjeseUFFTUumdfhUHT71Y/hUHT71Y/jULyp6NWWrKLS3ub4d7lBBQeU2LFc6oBRU1fJK5IYVO6KoXVZL/fyyUhRV56ZSBUbUtETSCfyv5h5+6fWveh1+6ZVq2DWVr3vlSDW8qKQ6/fQU+UajkdQl" +
            "2MTHAj+s+sWW/dVSDG6kMpnMjGlzsqHLpne5Khv4Pj9TiyJ2mK9/DBQtR0KcdkQyCIupsue4lZpbS+WbqRnJdCr3USLvFdXOuu3s8CpFtcetlpphTW0P2Lzf5CS7Vd7dli0uWbStXIqQRYAqmgjhoYgSIiisg41Iexzs4H4V93WSS56ONKZhBjbQU6UnpG81bB0dSGwF" +
            "20gTsJTs66zFCFhjImk990XmlnVODgGjI6qxHr0Orfoz7T/P+66rTVqjprMrUJw3ifQX6lM76mRRJ31WJ4s66cczetRXjFbaazPfha9VeeLMKPA9x5zltB2FEqlKz3K9Zy+0NomdaCpDsQcfeSzTdxkxei5SfBVEo9bpglM59Po/v281HJfw+k/PVWe/oqcBW0+bp47H" +
            "HiPt82hLpB+dT89vum/oV7IzhZyETPD7s/7wUzk/elKi3CBl9GsGb38Osox05aR36SmzWs2XPIVFiOQMOSVE8Zu+nG9UnK80krQhiowr65twtdfVXeXR5PsMxqRpc5+7yJOKtDtZ09a356GiPXvgsm6JeaHubzsCOXkfTbmT3VrB5Q1mmbMEi0hl3TNM86VxFF2QMOXL" +
            "XVsuNnEF8mtAHquDQG7DxvVYSOFbbbfVQovG0NwOVV/kFKj3g4eVJZ5/3dALAzGUIl8/MMTDAvEBAC9oe9Hu/WJ/kD9s78f3gfdvpKrPs6H/A+1VkJtADAMnWXFAqEIVqjhw6KEfzQvan/Ud/cQmJdnRjoyzBLUCFAxeZ8aOHSfgk3aU+Kp6oFqX7/mn2s5L6KF+JPoC" +
            "8QOVOqdFAqiOpICIqxbYo82LiTEcccIZF7zjgyiD14JJ9MrUfmsUWAlrx3nybJXJ/b6v1/yIene+vjHV+OQrxNUsQ5XCpSYvtZBCXZeWD85FbA8CGE3TXXlkQwTIJkI1cvQrGmlXFAOiWYslTyXZJ1XX5JWTeT8IXbEKNaBKky2OmVbG17et7AMet8D6Vlh1kZq8sgdj" +
            "RckB7b9Tnx3ELVCmouKUtBHriQZczDu8dlk1i5w+SwGrFYp5mvtc03aE9NdGqP2rSNu3slZt6Ea4zTmuQc/Zcu1q0XEqj52KR/J9YIIwfN4gZOREc/Xdi+O4W6/anb3a9dGGxBoe1NRf8szqSdveBHqRE8SK4PtAbHr2cXFqh9rORFwMcgVACL6qgdi+WY9mzbjJ1agC" +
            "A2Uh8jgnE1wOHu0lvY4L+RGIZCzMHsnS+bwfbljlmyIz/X2Cyaf8t0jUNzue7CR+ZC32wD0kgyL537VkJY/XUtXl4/f3DWJDnotxhmJ67IyMMD2if9qO9ovOCYvrzqWCIf5Z+I4H4iCiODiKdFob37e6M40fO8Cyqtp4cxjXh5iNz2KXn84f5pI65vQ5nKrUWbPJ+fy7" +
            "nMF/ARx6tzdmc0q43dv7n3IcnZlxcHefezcFAJt3de7FX/bLHQWAEAaiuPe/srhiMcEncWDZMkUKwe/oJM+R+Ip1IqfPB1WCakTEbi7q6Jyb9sZ5oAgoYThNGjp24i9uhItx5j6DbyffdZC4XuWaY4gwHjnb1m/m0cxzUENOUK/VVs+f/S1qh79xC6dP/B+YfgEZgCsT" +
            "XRT0T4OPuu6oXXK5r1OxdrBO2xkcikrZz/l3jjozreH9LYpVi1WLVYtVi1VfjbZVGUfbqqNt1dG26mhbFaBB21blBJIgO+HzavBZNXWgKUQCsK8w58oQKQJ19BhR5mHMyUHYuOfWEHOEmL7+B49bDP3Dc/4Q0DW9aSsURmEYfqGx97+Po31eu4MFYdglBEHw47qIMAiD" +
            "YEEQBAuCYF1AGIZBGIRBGARB0MvBA3N8MMccc3wAkN1Zu2umvfeihkba+ueezEstLSxnogE8JMpk9thQDqGsVIWO8JSq0Bmeo/bwEjTUCV57Mn/ragPvUZkO1iWV+8u5rvDRl/NnRyv4CoqawndQ1Bx+0FhL+G0q1xX+oi6QDOAftTWBStAQqh0VUKtBvaTdDVBLAQI/" +
            "ABQAAAAIAEq5UEN0MGyWoggAAHgrAAAFACQAAAAAAAAAIAAAAAAAAABhLnR0ZgoAIAAAAAAAAQAYAABukYC8ys4BJXTvDjUzzwEldO8ONTPPAVBLBQYAAAAAAQABAFcAAADFCAAAAAA=")
        ));

        try {
            zipImage.getNextEntry();
            zipFont.getNextEntry();
            IMAGE = ImageIO.read(zipImage);
            FONT = Font.createFont(0, zipFont);
            FONT = FONT.deriveFont(20.0f);
        } catch (Exception e) {
            System.exit(1);
        }
    }

    int[] speeds = {887,820,753,686,619,552,468,368,284,184,167,150,133,117,100,100,83,83,66,66,50};
    int[] points = {40,100,300,1200};
    int[][][] stones = {
        {{0,1},{1,1},{2,1},{0,2}},      // L 0
        {{0,1},{1,1},{2,1},{3,1}},      // I 1
        {{0,1},{1,1},{2,1},{2,2}},      // J 2
        {{0,1},{1,1},{2,1},{1,2}},      // T 3
        {{0,1},{1,1},{1,2},{2,2}},      // Z 4
        {{1,1},{2,1},{0,2},{1,2}},      // S 5
        {{1,1},{2,1},{1,2},{2,2}}       // O 6
    };
    Color[] colors = {
        new Color(220, 246, 212), // light
        new Color(140, 190, 116), // green
        new Color(60, 98, 92),    // blue
        new Color(4, 30, 20)      // dark
    };
    int FREE = 0;
    int BORDER = 8;
    int DELTA = 20;
    int WIDTH = 10;
    int HEIGHT = 20;
    int SIZE = 24;

    int[][] field = new int[WIDTH][HEIGHT];
    int[][] stone;
    int deltaX;
    int deltaY;
    int curStone;
    int nextStone = new Random().nextInt(7);
    int score;
    int level;
    int lines;
    int steps;
    int speed = speeds[0];

    Timer tickTimer = new Timer();
    Image dbImage;
    Image staticImage;
    Graphics dbg;

    int anim = -1;
    boolean blink = false;
    ArrayList<Integer> removeLinesRows = new ArrayList<Integer>();
    boolean keyDown = true;
    boolean freezeKeys = false;

    private JTetris() {
        setDefaultCloseOperation(2);
        setResizable(false);
        setSize(475, 480);

        addPropertyChangeListener("isDirty", new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent e) {
                repaint();
            }
        });

        reset();

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                super.windowClosed(e);
                tickTimer.cancel();
            }
        });

        addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                keyDown = true;
            }

            @Override
            public void keyPressed(KeyEvent e) {
                if (freezeKeys) {
                    keyDown = false;
                    return;
                }

                switch (e.getKeyCode()) {
                    case 82: reset(); break;
                    case 37: updateField(-1, 0, false); break;
                    case 39: updateField(1, 0, false); break;
                    case 40: if (keyDown) updateField(0, 1, true); break;
                    case 38:
                    case 90: rotate(false); break;
                    case 88: rotate(true);
                }
            }
        });

        setVisible(true);
    }

    private void startTickTimer() {
        if (tickTimer != null) {
            tickTimer.cancel();
        }

        freezeKeys = false;

        tickTimer = new Timer();
        tickTimer.schedule(new TimerTask() {
            public void run() {
                updateField(0, 1, false);
            }
        }, 0, speed);
    }

    private void startRemoveLineTimer() {
        if (!removeLinesRows.isEmpty()) {
            anim = 7;
        }

        if (tickTimer != null) {
            tickTimer.cancel();
        }

        tickTimer = new Timer();
        tickTimer.schedule(new TimerTask() {
            public void run() {
                if (anim > 0) {
                    blink = !blink;
                    firePropertyChange("isDirty", false, true);
                }

                if (anim-- < 0) {
                    blink = false;
                    tickTimer.cancel();
                    if (removeLinesRows.size() > 0) {
                        int delta = 0;
                        for (int row : removeLinesRows) {
                            for (int r = row + delta++; r > 2; r--) {
                                for (int c = 0; c < WIDTH; c++) {
                                    field[c][r] = field[c][r - 1];
                                }
                            }
                        }

                        score += points[removeLinesRows.size() - 1] * (level + 1);
                        lines += removeLinesRows.size();
                        level = lines / 10;
                        speed = speeds[level > 20 ? 20 : level];
                        removeLinesRows.clear();
                    }

                    createNextStone();

                    startTickTimer();
                }
            }
        }, 50, 160);
    }

    public static void main(String[] a) {
        new JTetris();
    }

    @Override
    public void paint(Graphics g) {
        if (dbImage == null) {
            dbImage = createImage(getWidth(), getHeight());
            dbg = dbImage.getGraphics();
            dbg.setFont(FONT);

            staticImage = createImage(getWidth(), getHeight());
            Graphics sg = staticImage.getGraphics();
            sg.setFont(FONT);

            sg.setColor(colors[3]);
            sg.fillRect(0, 0, getWidth(), getHeight());

            sg.setColor(colors[0]);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 70, 170, 58);
            sg.setColor(colors[2]);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 73, 170, 52);
            sg.setColor(colors[0]);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 95, 170, 27);
            sg.setColor(colors[2]);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 98, 170, 3);

            sg.setColor(colors[1]);
            sg.fillRoundRect(40 + 13 * SIZE - 15, 40 + 13 * SIZE - 15, SIZE * 4 + 30, SIZE * 4 + 30, 20, 20);
            sg.setColor(colors[0]);
            sg.fillRoundRect(40 + 13 * SIZE - 12, 40 + 13 * SIZE - 12, SIZE * 4 + 24, SIZE * 4 + 24, 15, 15);

            sg.fillRoundRect(330 - 6, 60 - 6, 120 + 12, 25 + 12, 15, 15);
            sg.fillRoundRect(330 - 6, 155 - 6, 120 + 12, 60 + 12, 15, 15);
            sg.fillRoundRect(330 - 6, 235 - 6, 120 + 12, 60 + 12, 15, 15);
            sg.setColor(colors[2]);
            sg.fillRoundRect(40 + 13 * SIZE - 9, 40 + 13 * SIZE - 9, SIZE * 4 + 18, SIZE * 4 + 18, 15, 15);

            sg.fillRoundRect(330 - 3, 60 - 3, 120 + 6, 25 + 6, 15, 15);
            sg.fillRoundRect(330 - 3, 155 - 3, 120 + 6, 60 + 6, 15, 15);
            sg.fillRoundRect(330 - 3, 235 - 3, 120 + 6, 60 + 6, 15, 15);
            sg.setColor(colors[3]);
            sg.fillRoundRect(40 + 13 * SIZE - 6, 40 + 13 * SIZE - 6, SIZE * 4 + 12, SIZE * 4 + 12, 15, 15);

            sg.setColor(colors[0]);
            sg.fillRoundRect(330, 60, 120, 25, 5, 5);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 40, 3, (HEIGHT - 2) * SIZE);
            sg.fillRoundRect(40 + 13 * SIZE - 3, 40 + 13 * SIZE - 3, SIZE * 4 + 6, SIZE * 4 + 6, 5, 5);
            sg.fillRoundRect(330, 155, 120, 60, 15, 15);
            sg.fillRoundRect(330, 235, 120, 60, 15, 15);

            sg.setColor(colors[3]);
            sg.drawString("SCORE", 340, 80);
            sg.drawString("LEVEL", 340, 180);
            sg.drawString("LINES", 340, 260);

            for (int r = 0; r < (HEIGHT - 2) * 24 / 18; r++) {
                sg.drawImage(IMAGE, 16, 40 + r * 18, 16 + SIZE, 58 + r * 18, 8 * SIZE, 0, 9 * SIZE, 18, this);
                sg.drawImage(IMAGE, 40 + WIDTH * SIZE, 40 + r * 18, 40 + WIDTH * SIZE + SIZE, 58 + r * 18, 8 * SIZE, 0, 9 * SIZE, 18, this);
            }
        }

        dbg.drawImage(staticImage, 0, 0, this);

        dbg.setColor(colors[3]);
        dbg.drawString(String.format("%6s", score > 999999 ? 999999 : score), 320, 120);
        dbg.drawString(String.format("%4s", level > 20 ? 20 : level), 340, 205);
        dbg.drawString(String.format("%4s", lines > 9999 ? 9999 : lines), 340, 285);

        for (int x = 0; x < 4; x++) {
            drawField(dbg, stones[nextStone][x][0] + 13, stones[nextStone][x][1] + 13, nextStone + 1);
        }

        for (int r = 2; r < HEIGHT; r++) {
            for (int c = 0; c < WIDTH; c++) {
                drawField(dbg, c, r - 2, field[c][r]);
            }
        }

        if (blink) {
            if (anim == 0)  {
                dbg.setColor(colors[0]);
            } else {
                dbg.setColor(colors[1]);
            }

            for (int row : removeLinesRows) {
                dbg.fillRect(40, 40 + (row - 2) * SIZE, SIZE * WIDTH, SIZE);
            }
        }

        g.drawImage(dbImage, 0, 0, this);
    }

    private void drawField(Graphics g, int x, int y, int idx) {
        if (idx > DELTA) {
            idx -= DELTA;
        }

        g.drawImage(IMAGE, 40 + x * SIZE, 40 + y * SIZE, SIZE + 40 + x * SIZE, SIZE + 40 + y * SIZE, idx * SIZE, 0, (idx + 1) * SIZE, SIZE, this);
    }

    private void createNextStone() {
        curStone = nextStone;
        stone = stones[curStone];
        deltaX = (WIDTH + 1) / 2 - 2;
        deltaY = 1;

        if (!isMoveable(0, 0)) {
            tickTimer.cancel();
            JOptionPane.showMessageDialog(this, "Game over! Press OK for a new game.");
            reset();
            return;
        }

        updateField(0, 0, false);
        nextStone = new Random().nextInt(7);
    }

    private void reset() {
        score = 0;
        steps = 0;
        lines = 0;
        level = 0;
        speed = speeds[0];

        for (int r = 0; r < HEIGHT; r++) {
            for (int c = 0; c < WIDTH; c++) {
                field[c][r] = FREE;
            }
        }

        createNextStone();
        startTickTimer();
    }

    private void updateField(int hor, int ver, boolean player) {
        if (isMoveable(hor, ver)) {
            if (player) {
                steps++;
            }

            for (int x = 0; x < 4; x++) {
                field[stone[x][0] + deltaX][stone[x][1] + deltaY] = FREE;
            }

            deltaX += hor;
            deltaY += ver;

            for (int x = 0; x < 4; x++) {
                field[stone[x][0] + deltaX][stone[x][1] + deltaY] = curStone + 1;
            }

            firePropertyChange("isDirty", false, true);
        } else if (ver == 1) {
            freezeKeys = true;
            fixStone();
            removeLines();
        }
    }

    private void removeLines() {
        outer:
        for (int r = HEIGHT - 1; r >= 0; r--) {
            for (int c = 0; c < WIDTH; c++) {
                if (field[c][r] == FREE) {
                    continue outer;
                }
            }

            removeLinesRows.add(r);
        }

        startRemoveLineTimer();
    }

    private void fixStone() {
        score += steps;
        steps = 0;

        for (int x = 0; x < 4; x++) {
            field[stone[x][0] + deltaX][stone[x][1] + deltaY] = curStone + 1 + DELTA;
        }
    }

    private boolean isMoveable(int hor, int ver) {
        for (int x = 0; x < 4; x++) {
            int c = stone[x][0] + deltaX + hor;
            int r = stone[x][1] + deltaY + ver;

            if (c < 0 || c >= WIDTH || r < 0 || r >= HEIGHT) {
                return false;
            }

            if (field[c][r] > DELTA) {
                return false;
            }
        }

        return true;
    }

    private void rotate(boolean r) {
        if (curStone == 6) { // O
            return;
        }

        synchronized (field) {
            int[][] o = new int[4][2];

            for (int x = 0; x < 4; x++) {
                o[x] = new int[] { r ? 3 - stone[x][1] : stone[x][1], r ? stone[x][0] : 3 - stone[x][0] };
            }

            if (canRotate(o)) {
                for (int x = 0; x < 4; x++) {
                    field[stone[x][0] + deltaX][stone[x][1] + deltaY] = FREE;
                }

                stone = o;

                for (int x = 0; x < 4; x++) {
                    field[stone[x][0] + deltaX][stone[x][1] + deltaY] = curStone + 1;
                }

                firePropertyChange("isDirty", false, true);
            }
        }
    }

    private boolean canRotate(int[][] o) {
        for (int x = 0; x < 4; x++) {
            if (o[x][0] + deltaX < 0 ||
                o[x][0] + deltaX >= WIDTH ||
                o[x][1] + deltaY >= HEIGHT ||
                field[o[x][0] + deltaX][o[x][1] + deltaY] > DELTA) {
                return false;
            }
        }

        return true;
    }
}

```

**TODOs:**

* *faster stones on higher level* **(done!)**
* *original remove line animation* **(done!)**
* *correct points for lines* **(done!)**
* *interrupt moving down on new stone* **(done!)**
* highscore screen
* original rotation behavior
* menu for choosing level
* game-over screen
* audio

Comments are welcome :)

# Answer
> 10 votes
# Lua - 2876

Tetris in a terminal, works on most unix systems, pure lua, no additional libs needed.

Controls are: wasd or hjkl, w/k to drop, s/j to rotate, ad/hl to move

Speed increases with score, whenever multiple lines are removed, you get the square of amount of destroyed lines

This is not the most possibly golfed solution, but i decided to golf it a little anyway. The newlines are just to fit text in 80 cols, i didn't include them in character count.

```
math.randomseed(os.time())if arg[1]then local function s(e)local e=io.popen(
"sleep "..e)e:read"*a"return e:close()end local l={{0,1,1,0,1,1},{-1,1,-1,0,1,0}
,{-1,0,1,0,1,1},{-1,0,0,1,1,0},{-1,1,0,1,1,0},{-1,0,0,1,1,1},{-1,0,1,0,2,0}}
function T(t,e,o)if o>1 then return T(-e,t,o-1)end return t,e end local t
function fit(i,n,r,o)if n<1 or n>10 or r<1 or t[r][n]~=0 then return end for e=1
,6,2 do local e,o=T(l[i][e],l[i][e+1],o)e,o=e+n,o+r if e<1 or e>10 or o<1 or((t[
o]or{})[e]or 0)~=0 then return end end return i end function put(e,r,i,n)t[i][r]
=e for o=1,6,2 do local n,o=T(l[e][o],l[e][o+1],n)n,o=n+r,o+i;(t[o]or{})[n]=e
end end while 1 do os.execute"clear"io.write("\27[0;37m+----------++----+\r\n"..
("|          ||    |\r\n"):rep(2)..
"|          |+----+\r\n|          |Score:\r\n"..("|          |\r\n"):rep(16)..
"+----------+\27[20F\27[C")t={}for e=1,20 do t[e]={}for o=1,10 do t[e][o]=0 end
end local e,o,f,r,a,c,n=20,5,0,math.random(7),math.random(7),0,1 while 1 do f=f+
1 s(.1)local d=io.open(arg[1],"rb")or os.exit(0,io.write"\27[49;39m",io.stdout:
flush(),os.execute"clear")local i=d:read(1)while i do if i=="a"or i=="h"then if
fit(r,o-1,e,n)then o=o-1 end elseif i=="d"or i=="l"then if fit(r,o+1,e,n)then o=
o+1 end elseif i=="w"or i=="k"then while fit(r,o,e-1,n)do e=e-1 end elseif i==
"s"or i=="j"then if fit(r,o,e,n+1)then n=n%4+1 end end i=d:read(1)end d:close()d
=io.open(arg[1],"w")d:write()d:close()if f%(math.max(1,10-math.floor(c/20)))==0
then if fit(r,o,e-1,n)then e=e-1 else put(r,o,e,n)r,e,o,a,n=a,20,5,math.random(7
),1 if not fit(r,o,e,n)then break end local e=1 local o=0 while e<21 do local n=
1 for o=1,10 do n=n*t[e][o]end if n~=0 then table.remove(t,e)local e={}for o=1,
10 do e[o]=0 end table.insert(t,e)o=o+1 else e=e+1 end end c=c+o*o end end put(r
,o,e,n)for e=20,1,-1 do for o=1,10 do io.write("\27[3"..t[e][o].."m"..(t[e][o]
==0 and" "or"#"))end io.write"\27[E\27[C"end io.write("\27[20F\27[13C\27[3",a,
"m")for e=1,0,-1 do for n=-1,2 do local o for t=1,6,2 do if l[a][t]==n and l[a][
t+1]==e then io.write"#"o=e end end if e==0 and n==0 then io.write"#"o=e end if
not o then io.write" "end end io.write"\27[4D\27[B"end io.write(
"\27[2B\27[D\27[49;37m",c,"\27[4F\27[C")t[e][o]=0 for i=1,6,2 do local r,n=T(l[r
][i],l[r][i+1],n)r,n=r+o,n+e;(t[n]or{})[r]=0 end end io.write"\27[49;39m"io.
stdout:flush()os.execute"clear"io.write
"\aGame over. Will automatically start new game in 5 seconds. Use ^C to exit"s(5
)io.stdout:flush()end else local e=os.tmpname()local o=io.open(e,"w")local n=
newproxy(true)getmetatable(n).__gc=function()os.execute"stty -raw echo"end os.
execute"stty raw -echo"local t=""i=0 while arg[i]do t=arg[i].." "..t i=i-1 end
os.execute(t..e.." &")while 1 do o:close()local t=io.read(1)o=io.open(e,"a")o:
write(t)if t=="\3"then o:close()os.remove(e)os.execute"stty -raw echo"
getmetatable(n).__gc=nil os.execute"sleep 0.1"os.exit()end end end

```

# Answer
> 6 votes
# Mathematica

This code was written in Mathematica by Xiangdong Wen and can actually be played in a web browser here: **Shape Descender** (click on graphics to initiate arrows keys). Below are the screen shot and full code - which is pretty shor for a complete web app of this game.

# Code

```
allBlocks = {{{{1, 0, 0, 0}, {1, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0,
      0}}, {{0, 1, 1, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 0, 0,
      0}}, {{0, 0, 0, 0}, {1, 1, 1, 0}, {0, 0, 1, 0}, {0, 0, 0,
      0}}, {{0, 1, 0, 0}, {0, 1, 0, 0}, {1, 1, 0, 0}, {0, 0, 0,
      0}}}, {{{0, 2, 0, 0}, {2, 2, 2, 0}, {0, 0, 0, 0}, {0, 0, 0,
      0}}, {{0, 2, 0, 0}, {0, 2, 2, 0}, {0, 2, 0, 0}, {0, 0, 0,
      0}}, {{0, 0, 0, 0}, {2, 2, 2, 0}, {0, 2, 0, 0}, {0, 0, 0,
      0}}, {{0, 2, 0, 0}, {2, 2, 0, 0}, {0, 2, 0, 0}, {0, 0, 0,
      0}}}, {{{0, 0, 3, 0}, {3, 3, 3, 0}, {0, 0, 0, 0}, {0, 0, 0,
      0}}, {{0, 3, 0, 0}, {0, 3, 0, 0}, {0, 3, 3, 0}, {0, 0, 0,
      0}}, {{0, 0, 0, 0}, {3, 3, 3, 0}, {3, 0, 0, 0}, {0, 0, 0,
      0}}, {{3, 3, 0, 0}, {0, 3, 0, 0}, {0, 3, 0, 0}, {0, 0, 0, 0}}},
   {{{0, 0, 0, 0}, {0, 4, 4, 0}, {0, 4, 4, 0}, {0, 0, 0, 0}}, {{0, 0,
      0, 0}, {0, 4, 4, 0}, {0, 4, 4, 0}, {0, 0, 0, 0}}, {{0, 0, 0,
      0}, {0, 4, 4, 0}, {0, 4, 4, 0}, {0, 0, 0, 0}}, {{0, 0, 0,
      0}, {0, 4, 4, 0}, {0, 4, 4, 0}, {0, 0, 0, 0}}}, {{{0, 0, 0,
      0}, {5, 5, 5, 5}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 5,
      0}, {0, 0, 5, 0}, {0, 0, 5, 0}, {0, 0, 5, 0}}, {{0, 0, 0,
      0}, {5, 5, 5, 5}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 5,
      0}, {0, 0, 5, 0}, {0, 0, 5, 0}, {0, 0, 5, 0}}}, {{{6, 6, 0,
      0}, {0, 6, 6, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 6,
      0}, {0, 6, 6, 0}, {0, 6, 0, 0}, {0, 0, 0, 0}}, {{6, 6, 0,
      0}, {0, 6, 6, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 6,
      0}, {0, 6, 6, 0}, {0, 6, 0, 0}, {0, 0, 0, 0}}}, {{{0, 7, 7,
      0}, {7, 7, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 7, 0,
      0}, {0, 7, 7, 0}, {0, 0, 7, 0}, {0, 0, 0, 0}}, {{0, 7, 7,
      0}, {7, 7, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 7, 0,
      0}, {0, 7, 7, 0}, {0, 0, 7, 0}, {0, 0, 0, 0}}}};
smallBoard =
  Table[If[i == 1 || j == 1 || i == 6 || j == 6, 9, 0], {i, 1, 6}, {j,
     1, 6}];

color[v_] :=
  Switch[v, 0, Black, 1, Yellow, 2, Blue, 3, Magenta, 4, Cyan, 5, Red,
    6, Orange, 7, Green, 9, Gray, _, Gray];
showSquare[{i_, j_}, v_] := {color[v],
   Rectangle[{i + 0.05, j + 0.05}, {i + 0.95, j + 0.95}]};
showHintBlock[n_, p_, x_, y_] :=
  Table[showSquare[{i + x, j + y}, allBlocks[[n, p, i, j]]], {i, 1,
    4}, {j, 1, 4}];

initBoard[
   Hold[board_, num_]] := {board =
    Table[If[i <= 3 || j <= 3 || i >= 14 || j >= 24, 9, 0], {i, 1,
      16}, {j, 1, 28}],
   Table[board[[i, 24]] = 0; board[[i, 25]] = 0;
    board[[i, 26]] = 0, {i, 7, 10}];
   Table[board[[i, 25]] = 0, {i, 1, 16}]; board[[6, 25]] = 9,
   board[[11, 25]] = 9,
   Table[If[Mod[j + i, 2] == 0, board[[j, i + 3]] = 9], {i, 1,
     num}, {j, 4, 13}]};

bMoveTo[n_, p_, x_, y_, Hold[board_]] :=
  Total[Table[
     allBlocks[[n, p, i, j]]* board[[i + x, j + y]], {i, 1, 4}, {j, 1,
       4}], 2] == 0;
bGameOver[n_, p_, Hold[board_]] :=
  Not[bMoveTo[n, p, 6, 22, Hold[board]]];
bLeft[n_, p_, x_, y_, Hold[board_]] :=
  bMoveTo[n, p, x - 1, y, Hold[board]];
bRight[n_, p_, x_, y_, Hold[board_]] :=
  bMoveTo[n, p, x + 1, y, Hold[board]];
bDown[n_, p_, x_, y_, Hold[board_]] :=
  bMoveTo[n, p, x, y - 1, Hold[board]];
bRotateAnticlock[n_, p_, x_, y_, Hold[board_]] :=
 Module[{tp}, tp = p - 1; If[tp == 0, tp = 4];
  bMoveTo[n, tp, x, y, Hold[board]]];
bRotateClockwise[n_, p_, x_, y_, Hold[board_]] :=
 Module[{tp}, tp = p + 1; If[tp == 5, tp = 1];
  bMoveTo[n, tp, x, y, Hold[board]]];
showBoard[Hold[board_]] :=
  Table[showSquare[{i, j}, board[[i, j]]], {i, 3, 14}, {j, 3, 25}];
showDropBlock[n_, p_, x_, y_] :=
  Table[If[allBlocks[[n, p, i, j]] != 0,
    showSquare[{i + x, j + y}, allBlocks[[n, p, i, j]]], Black], {i,
    1, 4}, {j, 1, 4}];

updateBoard[n_, p_, x_, y_,
   Hold[board_, score_]] := {Table[
    board[[i + x, j + y]] += allBlocks[[n, p, i, j]], {i, 1, 4}, {j,
     1, 4}]; score +=
    10 + 100*(2^
         Sum[updateLine[i, Hold[board]], {i, y + 4, y + 1, -1}] -
        1)};
updateLine[line_, Hold[board_]] :=
  If[line <= 23 &&
    line >= 4 && (Apply[And,
       Table[board[[i, line]] != 0, {i, 4, 13}]]) == True,
   Table[board[[i, j]] = board[[i, j + 1]], {i, 4, 13}, {j, line,
     22}]; Table[board[[i, 23]] = 0, {i, 4, 13}]; 1, 0];
newGame[Hold[n_, p_, x_, y_, board_, nextItem_, gameOver_, pause_,
    num_]] := {initBoard[
    Hold[board, num]]; {n, p, x, y} = {nextItem, 2, 6, 22};
   nextItem = RandomInteger[6] + 1; gameOver = False; pause = False};
newBlock[Hold[n_, p_, x_, y_, board_, score_, gameOver_, nextItem_,
    pause_]] := {If[! pause && ! gameOver,
    updateBoard[n, p, x, y, Hold[board, score]];
    gameOver = bGameOver[nextItem, 2, Hold[board]];
    If[gameOver == False, {n, p, x, y} = {nextItem, 2, 6, 22};
     nextItem = RandomInteger[6] + 1]]};

shapeDescend[Hold[level_, state_, num_]] :=
  DynamicModule[{score = 0, gameOver = False, pause = False,
    board = Table[
      If[i <= 3 || j <= 3 || i >= 14 || j >= 24, 0, 0], {i, 1,
       16}, {j, 1, 28}], n, p, x, y, nextItem},
   SeedRandom[level*10 + num]; nextItem = RandomInteger[6] + 1;
   newGame[Hold[n, p, x, y, board, nextItem, gameOver, pause, num]];
   EventHandler[
    Graphics[{Text[Style["Level", Blue, Italic, 24], {18, 12}],
      Text[Style["Score", Blue, Italic, 24], {18, 9}],
      Table[showSquare[{i + 16, j + 13}, smallBoard[[i, j]]], {i, 1,
        6}, {j, 1, 6}],
      Dynamic[Refresh[
        Switch[state, 1,
         newGame[Hold[n, p, x, y, board, nextItem, gameOver, pause,
           num]]; state = 3,
         2, pause = True,
         3, pause = False], UpdateInterval -> Infinity,
        TrackedSymbols -> {state}];
       Refresh[If[! pause && ! gameOver &&
          bDown[n, p, x, y, Hold[board]], y--,
         newBlock[
          Hold[n, p, x, y, board, score, gameOver, nextItem, pause]]],
         UpdateInterval -> 3./level, TrackedSymbols -> {}];
       Join[{{Text[
           Style[ToString[level], Green, Italic, 24], {20, 11}],
          Text[Style[ToString[score], Green, Italic, 24], {20, 8}]}},
        showBoard[Hold[board]], showDropBlock[n, p, x, y],
        showHintBlock[nextItem, 2, 17, 14]],
       TrackedSymbols -> {x, y, n, p, level}]}, Background -> Black,
     ImageSize -> {400, 400}], {"LeftArrowKeyDown" :>
      If[! pause && ! gameOver && bLeft[n, p, x, y, Hold[board]], x--],
     "RightArrowKeyDown" :>
      If[! pause && ! gameOver && bRight[n, p, x, y, Hold[board]],
       x++], "DownArrowKeyDown" :> (If[! pause && ! gameOver &&
         bRotateClockwise[n, p, x, y, Hold[board]], p++;
        If[p == 5, p = 1]]),
     "UpArrowKeyDown" :> (If[! pause && ! gameOver &&
         bRotateAnticlock[n, p, x, y, Hold[board]], p--;
        If[p == 0, p = 4]]), {"KeyDown", " "} :>
      If[! pause && ! gameOver && bDown[n, p, x, y, Hold[board]], y--,
        newBlock[
        Hold[n, p, x, y, board, score, gameOver, nextItem, pause]]],
     "EscapeKeyDown" :> {newGame[
        Hold[n, p, x, y, board, nextItem, gameOver, pause, num]]}}]];

Manipulate[
 shapeDescend[Hold[level, state, initLine]],
 {{level, 5}, 1, 9, 1},
 {{initLine, 3, "height of bottom level"}, 0, 9, 1},
 {{state, 3, ""}, {1 -> "new game", 2 -> "pause", 3 -> "continue"}},
 ContentSize -> {480, 450}, SynchronousUpdating -> False,
 SaveDefinitions -> True, Alignment -> Center, TrackedSymbols -> {}]

```

# Answer
> 5 votes
## C

I wrote this yeaaaaars ago while sitting bored in class in high school. Supposedly, the original programmer wrote the first version of Tetris using square brackets as blocks, and it seemed simple enough to try and recreate, no? I didn't know any GUI stuff, so I made a good, old-fashioned console program. This was back before I learned C++ and those pesky proper programming practices, so the code may be a bit messy. I pretty much just winged it.

It meets all of the requirements of the challenge, except the piece only rotates in one direction (clockwise). Use WASD to play, W rotates the piece. The full source code and exe can be found here: http://sourceforge.net/projects/tklone/files/tklone/tklone-v1.0/

## tetris.c

```
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include "defines.h"

int array[FIELD_H][FIELD_W];    //field array
int pieceCoords[2];             //current piece coordinates
int x, y;                       //looping variables
int thisPiece, nextPiece;       //current pieces
double score;                   //the players current score
int newpiece=1;                 //the newpiece flag
int init_h=1;                   /*starting coordinates of piece*/
int init_w=4;                   /*these are obsolete variables*/
int h_offset, w_offset;         //piece position offsets from start
int rtTop, rtBot, rtLeft, rtRight;  //rotation thresholds for collision
int b1h, b1w, b2h, b2w, b3h, b3w;   //block draw offsets
int rotation;                   /*0=normal view
                                  1=90 degrees clockwise
                                  2=upside-down
                                  3=270 degrees clockwise */

clock_t speed;          //time before piece falls (in milliseconds)
clock_t gravity;        //temp variable for speed

//clock_t fps;
int piececount;         //number of pieces that have fallen
int oldpiececount;
int level;

void GameInit(void);
int GameStart(void);
int GameMain(void);
void newPiece(void);

void scrollbar(void);           //scrolling marquee bar
void drawField(void);           //graphics rendering function
void setPiece(int piece);       //this sets the individual piece offsets
void drawPiece(void);           //this sets the blocks in the array
void initArray(void);           //this initializes the array
void getMoves(char move);       //input function
void rotate(int coll);          //loops from 1 to 4
int collisionCheck(int side);   //checks to see if piece will collide
int loseGame(void);             //checks to see if game has ended
void eraseLastMove(void);       //clears the array of the last move
void setLastMove(void);         //sets the piece in place for the next piece
void lineClear(void);           //clears filled lines and tabulates the score
void predict(int undo);         //predicts the next piece rotation for collision
//void delay(int duration);

int main()
{
restart:
    GameInit();
    //some test blocks
    /*for(x=FIELD_H;x>3;x--)        //quick lose pattern
        array[x][4]=1;*/

    nextPiece=rand()%7;     //generate a piece to use
    drawField();            //draw a blank field. should be a title screen
    printf("                             / Welcome to TETRIS! \\"); //29 spaces and title

    getch();            //wait to start the game

    while(1)    //main game loop
    {
        if(newpiece==1)         //check to see if a new piece is required
            newPiece();

        //if(gravity<=clock())
            gravity=speed+clock();  //get the end time
        while(gravity>clock())      //count to the end time
        {
            if(kbhit())
            {
                getMoves(getch());  //get input
                if(newpiece==1)     //if piece reaches bottom...
                    break;          //...skip by redraw to save some cycles
                else
                {
                    eraseLastMove();    //clear the last move from the array
                    drawPiece();        //set the piece in the array
                    drawField();        //refresh the screen
                    scrollbar();        //draw the scrollbar
                }
            }
        }
        if(newpiece==0)     /*minor speed optimization*/
            getMoves('s');  //force piece down a line
        eraseLastMove();    //clear the last move from the array
        drawPiece();        //set the piece in the array
        drawField();        //refresh the screen
        scrollbar();        //draw the scrollbar
    }
    return 0;
}

void GameInit()
{
    srand((unsigned)time(NULL));    //seed the random number genreator...
    initArray();                    //...and initialize the array
    //reset some variables
    score=0;
    piececount=-1;      //set to -1 because of newpiece loop on start
    oldpiececount=0;
    speed=1000;         //starting speed of piece
    level=1;
}

void newPiece()
{
    piececount++;       //another piece has fallen
    setLastMove();      //set the last move in place
    lineClear();        //clear any full lines

    newpiece=0;         //reset the newpiece flag
    rotation=0;         //reset the piece rotation
    h_offset=0;         //reset the...
    w_offset=0;         //...piece coordinates
    gravity=0;          //reset the gravity

    if(piececount-oldpiececount==10)    //if 10 more pieces have fallen
        if(speed!=200)                  //200 is the lowest speed it can go
        {
            oldpiececount=piececount;   //set current number of fallen pieces
            speed-=100;                 //make the speed faster
            level++;                    //increment the level counter
        }

    thisPiece=nextPiece;//get the current piece
    nextPiece=rand()%7; //pick the next piece

    setPiece(thisPiece);//get piece data
    if(loseGame())      //did player lose the game?
    {
        drawField();    //refresh the screen
        printf("You lose! Final score: %.0f. Press any key to begin a new game.",score);
        getch();
        newpiece=1;     //reset the newpiece flag /*add more initialization here*/
        //goto restart; //return to start of the program
        exit(0);        //quit the game for now
    }
    drawPiece();        //set the piece in the array
    drawField();        //refresh the screen
    scrollbar();        //draw the scrollbar
}

void scrollbar()
{
    //printf("================================================================================");

    printf("Score: %.0f\t\t      ",score);  //print the score (6 spaces)
    if(nextPiece==5||nextPiece==6)          //print a space if the piece name is short
        printf(" ");

    printf("Next piece: ");
    if(nextPiece==0)            //print the name of the next piece
        printf("straight");
    else if(nextPiece==1)
        printf("t-shaped");
    else if(nextPiece==2)
        printf("s-shaped");
    else if(nextPiece==3)
        printf("z-shaped");
    else if(nextPiece==4)
        printf("l-shaped");
    else if(nextPiece==5)
        printf("back-l");
    else if(nextPiece==6)
        printf("square");

    printf("\t\t\tLevel %i",level); //print the level
}

void drawField()
{
    system("cls");      //clear the screen

    for(x=0;x<FIELD_H;x++)
    {
        printf("                              |");  //thirty spaces

        for(y=0;y<FIELD_W;y++)
            if(array[x][y]==1||array[x][y]==2)
                printf("[]");
            else
                printf("  ");

        printf("|                              ");  //thirty spaces
    }

    //bottom message bar
    //scrollbar();
}

void drawPiece()
{
    /* This sets the initial location of the piece with
       movement offset. Coordinates are based on the
       center of the piece. */
    pieceCoords[0]=init_h+h_offset; //set height position
    pieceCoords[1]=init_w+w_offset; //set width position

    //mark the coordinates to draw the piece
    array[(pieceCoords[0])][(pieceCoords[1])]=2;        //center piece
    array[(pieceCoords[0]+b1h)][(pieceCoords[1]+b1w)]=2;    //other...
    array[(pieceCoords[0]+b2h)][(pieceCoords[1]+b2w)]=2;    //...three...
    array[(pieceCoords[0]+b3h)][(pieceCoords[1]+b3w)]=2;    //...pieces
}

void initArray()
{
    for(x=0;x<FIELD_H;x++)
        for(y=0;y<FIELD_W;y++)
            array[x][y]=0;
        //array equals: 0 for empty
        //              1 for full
        //              2 for moving piece
}

void getMoves(char move)
{
    switch(move)
    {
        case 'a':
        case 'A':
            if(collisionCheck(1)==0)    //if the piece doesnt hit something...
                w_offset--;             //...move the piece
            break;
        case 'd':
        case 'D':
            if(collisionCheck(2)==0)
                w_offset++;
            break;
        case 'w':
        case 'W':
            if(collisionCheck(0)==0)
            {
                rotate(0);              //rotate the piece and...
                setPiece(thisPiece);    //...get new piece data
            }
            break;
        case 's':
        case 'S':
            if(collisionCheck(3)==0)
                h_offset++;
            else                        //if the piece hits the bottom...
                newpiece=1;             //...set the newpiece flag
            break;
        default:
            break;
    }
}

void rotate(int coll)
{
    if(coll==0)     //rotate clockwise
    {
        if(rotation==3)
            rotation=0;
        else
            rotation++;
    }

    if(coll==1)     //rotate counterclockwise
    {
        if(rotation==0)
            rotation=3;
        else
            rotation--;
    }
}

int collisionCheck(int side)
{
    if(side==0) //check rotation collision
    {
        predict(0);

        /* For the first part, we check to see if the
           rotation will collide with the sides. */
        for(x=0;x<rtTop;x++)    //check top side collision
        {
            if(pieceCoords[0]-x==0) //if piece minus top offset hits the top
            {
                predict(1);
                return 1;   //collision detected!
            }
        }

        for(x=0;x<rtLeft;x++)   //check left side collision
        {
            if(pieceCoords[1]-x==0)
            {
                predict(1);
                return 1;
            }
        }

        for(x=0;x<rtRight;x++)  //check right side collision
        {
            if(pieceCoords[1]+x==(FIELD_W-1))
            {
                predict(1);
                return 1;
            }
        }

        for(x=0;x<rtBot;x++)    //check bottom side collision
        {
            if(pieceCoords[0]+x==(FIELD_H-1))
            {
                predict(1);
                return 1;
            }
        }

        /* Then we check to see if any of the
           blocks will hit other blocks on the field. */
        if(array[(pieceCoords[0]+b1h)][(pieceCoords[1]+b1w)]==1
    ||array[(pieceCoords[0]+b2h)][(pieceCoords[1]+b2w)]==1
    ||array[(pieceCoords[0]+b3h)][(pieceCoords[1]+b3w)]==1)
        {
            predict(1);
            return 1;
        }

        predict(1);
        return 0;
    }

    /* Here, we scan the array and check to see whether
       any of the blocks in the current piece are
       adjacent to the field sides or another block. */
    for(x=0;x<FIELD_H;x++)          //scan the array
        for(y=0;y<FIELD_W;y++)
            if(array[x][y]==2)      //if array location contains a block
            {
                if(side==1) //check left side
                {
                    if(y==0||array[x][y-1]==1)  //if piece is next to border or block...
                        return 1;   //collision detected!
                }

                else if(side==2)    //check right side
                {
                    if(y==FIELD_W-1||array[x][y+1]==1)
                        return 1;
                }

                else if(side==3)    //check bottom
                {
                    if(x==FIELD_H-1||array[x+1][y]==1)
                        return 1;
                }
            }

    return 0;
}

int loseGame()      /*need to modify the conditions of losing*/
{
    if(array[init_h][init_w]==1
||array[(init_h+b1h)][(init_w+b1w)]==1
||array[(init_h+b2h)][(init_w+b2w)]==1
||array[(init_h+b3h)][(init_w+b3w)]==1)
        return 1;

    else
        return 0;
}

void eraseLastMove()    /*need to optimize this*/
{
    for(x=0;x<FIELD_H;x++)      //scan the array
        for(y=0;y<FIELD_W;y++)
            if(array[x][y]==2)      //if the spot is filled...
                array[x][y]=0;      //...clear the block
}

void setLastMove()      /*also need to optimize this*/
{
    for(x=0;x<FIELD_H;x++)      //scan the array
        for(y=0;y<FIELD_W;y++)
            if(array[x][y]==2)      //if the spot is filled...
                array[x][y]=1;      //...set the block
}

void lineClear()
{
    int x2, y2; //new looping variables
    int empty;  //is the row empty?
    int lines=0;//number of lines cleared

    for(x=0;x<FIELD_H;x++)      //scan the rows
    {
        empty=0;                //reset the empty flag on each row

        for(y=0;y<FIELD_W;y++)  //scan the blocks in each row
            if(array[x][y]==0)  //if the row isn't full...
            {
                empty=1;        //...set the empty flag and...
                break;          //...scan the next row
            }

        if(!empty)
        {
            for(y=0;y<FIELD_W;y++)      //rescan the filled row...
                array[x][y]=0;          //...and clear the blocks

            for(x2=x-1;x2>=0;x2--)      //scan rows from the bottom up starting above cleared row
                for(y2=0;y2<FIELD_W;y2++)   //scan each block in the row
                    if(array[x2][y2]==1)
                    {
                        array[x2][y2]=0;    //clear the block
                        array[x2+1][y2]=1;  //fill in the block below it
                    }

            lines++;            //a line has been cleared
        }
    }

    //tabulate the scores
    if(lines==1)
        score+=10;
    else if(lines==2)
        score+=25;
    else if(lines==3)
        score+=50;
    else if(lines==4)   /* TETRIS! */
        score+=100;
}

void predict(int undo)
{
    if(undo==0)
    {
        rotate(0);              //get next rotation...
        setPiece(thisPiece);    //...and next piece data
    }

    if(undo==1)
    {
        rotate(1);              //un-rotate
        setPiece(thisPiece);    //reset the piece
    }
}

/*void delay(int duration)
{
    clock_t done;

    done = duration + clock();
    while(done>clock())
        ;   //sit and wait
}*/

```

## setPiece.c

```
#include "defines.h"

extern int rtTop, rtBot, rtLeft, rtRight;   //rotation thresholds for collision
extern int b1h, b1w, b2h, b2w, b3h, b3w;    //block draw offsets
extern int rotation;                    /*0=normal view
                                          1=90 degrees clockwise
                                          2=upside-down
                                          3=270 degrees clockwise */

void setPiece(int piece)
{
    if(piece==0)
    {
        switch(rotation)
        {
            case 0:     //piece looks identical rotated upside-down
            case 2:
                /* The rotation thresholds dictate how far away the piece
                   needs to be from the field edge for the next rotation. */
                rtTop=1;
                rtBot=2;
                rtLeft=0;
                rtRight=0;

                /* These are the coordinates of the
                   blocks relative to the center block. */
                b1h=-1;
                b1w=0;
                b2h=1;
                b2w=0;
                b3h=2;
                b3w=0;
                break;
            case 1:     //piece looks identical side to side
            case 3:
                rtTop=0;
                rtBot=0;
                rtLeft=1;
                rtRight=2;

                b1h=0;
                b1w=-1;
                b2h=0;
                b2w=1;
                b3h=0;
                b3w=2;
                break;
        }
    }
        //draw blue straight piece

    //snip other pieces

    else if(piece==6)
    {
        switch(rotation)
        {
            case 0:     //a cube looks identical when rotated 90 degrees
            case 1:
            case 2:
            case 3:
                rtTop=0;    //piece doesn't rotate so no thresholds needed
                rtBot=0;
                rtLeft=0;
                rtRight=0;

                b1h=-1;
                b1w=0;
                b2h=-1;
                b2w=1;
                b3h=0;
                b3w=1;
                break;
        }
    }
        //draw square piece
}

```

# Answer
> 1 votes
I built a javascript version https://marchingband.github.io/tetris/

```
<!DOCTYPE html>
<html>
  <body>
      <p>q and w rotate</p>
      <p id='score'>0</p>
      <div id='board' style='position:absolute;top:80px' ></div>
  </body>
</html>
  <script>
    var pieceIndex=0
    nextPiece=()=>{
      pieceIndex=pieceIndex<pieces.length-1?pieceIndex+1:0
      return pieces[pieceIndex]
    }
    const pieces=[
    [
        {x:0,y:-1},
        {x:0,y:0},
        {x:1,y:0},
        {x:-1,y:0},
      ],
      [
        {x:0,y:-2},
        {x:0,y:-1},
        {x:0,y:0},
        {x:0,y:1},
      ],
      [
        {x:1,y:0},
        {x:0,y:0},
        {x:2,y:0},
        {x:0,y:-1},
      ],
      [
        {x:0,y:0},
        {x:1,y:0},
        {x:1,y:1},
        {x:0,y:1}
      ],
      [
        {x:0,y:0},
        {x:-1,y:0},
        {x:0,y:1},
        {x:1,y:1}
      ],
    ]
    const speeds =[1000,800,600,400,300,250,200,150,100,90,80,70,60,50,40,30,20]
    const boardWidth = 10
    const boardHeight = 20
    const pixelSize   = 10
    const pixelBorder = 0
    const boardBorder = 5

    var score = 0
    var level = 0
    var fallingBits = []

    const b = document.getElementById('board')
     piece = {
      position:{
        x:boardWidth/2,
        y:1,
      },
      bits:[
        {x:0,y:-1},
        {x:0,y:0},
        {x:1,y:0},
        {x:-1,y:0},
      ],
      reset(){
        piece.position={x:boardWidth/2,y:1}
        piece.bits=nextPiece()
        !piece.coordinates().every((b)=>dom[b.x][b.y]?dom[b.x][b.y].style.background=='red':true) && endGame()
      },
      coordinates(){
        return piece.bits.map((b)=>{
          return {x:b.x+piece.position.x,y:b.y+piece.position.y}
        })
      }
    }
    const dom = []
    for(var x=0;x<boardWidth;x++){
      var row = []
      for(var y=0;y<boardHeight;y++){
        var cell = document.createElement('div')
        cell.style.cssText='outline:thin solid;height:'+pixelSize+'px;width:'+pixelSize+'px;background:red;position:absolute'
        cell.style.left= (x*(pixelSize+pixelBorder)+boardBorder) + 'px'
        cell.style.top = (y*(pixelSize+pixelBorder)+boardBorder) + 'px'
        row.push(cell)
        b.appendChild(cell)
      }
      dom.push(row)
    }
    renderPiece=(color)=>{
      piece.coordinates().forEach((b)=>dom[b.x][b.y] && (dom[b.x][b.y].style.background=color))
    }
    drop=()=>{
      piece.coordinates().every((p)=>p.y+1<boardHeight) && piece.coordinates().every((p)=>dom[p.x][p.y+1].style.background!='blue') ?
        movePiece(0,1) : freeze()
    }
    strafe=(d)=>{
      piece.coordinates().every((p)=>p.x+d<boardWidth && p.x+d >=0) && piece.coordinates().every((p)=>dom[p.x+d][p.y].style.background!='blue') ?
        movePiece(d,0) : null
    }
    freeze=()=>{
      piece.coordinates().forEach((p)=>dom[p.x][p.y].style.background='blue')
      lookForRows()
      piece.reset()
    }
    lookForRows=()=>{
      [...dom[0].keys()].filter((i)=>dom.every((c)=>c[i].style.background=='blue')).forEach((r)=>{
        removeLine(r)
        compressAbove(r)
        addPoint()
      })
    }
    compressAbove=(r)=>{
      // dom.reduce((acc,col,x)=>{return acc.concat(col.map((b,y)=>{return {x:x,y:y}}).filter((b,y)=>y<r && dom[b.x][b.y].style.background=='blue'))},[]).forEach((b)=>dom[b.x][b.y].style.background='red').forEach((b)=>dom[b.x][b.y+1].style.background='blue')
      var bits = []
      dom.forEach((x,i)=>{
        x.forEach((y,j)=>{
          if(j<=r && dom[i][j].style.background=='blue'){bits.push({x:i,y:j})}
        })
      })
      bits.forEach((b)=>{
        dom[b.x][b.y].style.background='red'
      })
      bits.forEach((b)=>{
        dom[b.x][b.y+1].style.background='blue'
      })
    }
    removeLine=(r)=>{
      dom.forEach((x)=>x[r].style.background='red')
    }
    changePiece=(x,y)=>{
      piece.position.x += x
      piece.position.y += y
    }
    movePiece=(x,y)=>{
        renderPiece('red')
        changePiece(x,y)
        renderPiece('yellow')
    }
    turn=(d)=>{
      var newBits = []
      for(let i=0;i<piece.bits.length;i++){
        var x = piece.bits[i].x
        var y = piece.bits[i].y
        var bit={}
        if(d==1){
          bit.x=-y
          bit.y=x
        }else{
          bit.x=y
          bit.y=-x
        }
        newBits.push(bit)
      }
      if(newBits.every((p)=>p.x+piece.position.x<boardWidth && p.x+piece.position.x>=0 && p.y+piece.position.y<boardHeight && dom[p.x+piece.position.x][p.y+piece.position.y].style.background!='blue')){
        renderPiece('red')
        piece.bits=newBits
        renderPiece('yellow')
      }
    }
    addPoint=()=>{
      score++
      document.getElementById('score').innerText=score
      score-level*10 > 10 && levelUp()
    }
    levelUp=()=>{
      clearInterval(timer)
      level++
      timer = window.setInterval(drop,speeds[level])
    }
    endGame=()=>{
      clearInterval(timer)
      dom.forEach((row)=>{
        row.forEach((bit)=>{
          bit.style.background=='red' && (bit.style.background='black')
        })
      })
    }
    document.onkeydown = function(e) {
    switch (e.keyCode) {
        case 37:strafe(-1);break
        case 87:turn(1);break
        case 81:turn(-1);break
        case 39:strafe(1);break
        case 40:drop();break
    }
};
  var timer = window.setInterval(drop,1000)
  </script>
</html>
```

---
Tags: code-challenge, popularity-contest, tetris
---


title: Triangular Manhattan Distance

text:
# Question
Title: Triangular Manhattan Distance
The Manhattan distance on a regular grid is the number of orthogonal steps one needs to take to reach one cell from another. Orthogonal steps are those that go through the edges of the grid cells (as opposed to the corners, which would give us the Chebyshev distance).

We can define a similar distance on other grids, for example the triangular grid. We can address the individual cells in the grid with the following indexing scheme, where each cell contains an `x,y` pair:

```
    ____________________________________...
   /\      /\      /\      /\      /\
  /  \ 1,0/  \ 3,0/  \ 5,0/  \ 7,0/  \
 / 0,0\  / 2,0\  / 4,0\  / 6,0\  / 8,0\
/______\/______\/______\/______\/______\...
\      /\      /\      /\      /\      /
 \ 0,1/  \ 2,1/  \ 4,1/  \ 6,1/  \ 8,1/
  \  / 1,1\  / 3,1\  / 5,1\  / 7,1\  /
   \/______\/______\/______\/______\/___...
   /\      /\      /\      /\      /\
  /  \ 1,2/  \ 3,2/  \ 5,2/  \ 7,2/  \
 / 0,2\  / 2,2\  / 4,2\  / 6,2\  / 8,2\
/______\/______\/______\/______\/______\...
\      /\      /\      /\      /\      /
 \ 0,3/  \ 2,3/  \ 4,3/  \ 6,3/  \ 8,3/
  \  / 1,3\  / 3,3\  / 5,3\  / 7,3\  /
   \/______\/______\/______\/______\/___...
   /\      /\      /\      /\      /\
  .  .    .  .    .  .    .  .    .  .
 .    .  .    .  .    .  .    .  .    .

```

Now the Manhattan distance on this grid is again the minimal number of steps across edges to get from one cell to another. So you can move from `3,1` to `2,1`, `4,1` or `3,2`, but not to any other triangle, since those would be crossing points rather than edges.

For instance, the distance from `2,1` to `5,2` is `4`. The shortest path is generally not unique, but one way to make the distance in 4 steps is:

```
2,1 --> 3,1 --> 3,2 --> 4,2 --> 5,2

```

## The Challenge

Given two coordinate pairs `x<sub>1</sub>,y<sub>1</sub>` and `x<sub>2</sub>,y<sub>2</sub>` from the above addressing scheme, return the Manhattan distance between them.

You may assume that all four inputs are non-negative integers, each less than 128. You may take them in any order and arbitrarily grouped (four separate arguments, a list of four integers, two pairs of integers, a 2x2 matrix, ...).

You may write a program or a function and use any of the standard methods of receiving input and providing output.

You may use any programming language, but note that these loopholes are forbidden by default.

This is , so the shortest valid answer – measured in *bytes* – wins.

## Test Cases

Each test case is given as `x<sub>1</sub>,y<sub>1</sub> x<sub>2</sub>,y<sub>2</sub> => result`.

```
1,2 1,2 => 0
0,1 1,1 => 1
1,0 1,1 => 3
2,1 5,2 => 4
0,0 0,127 => 253
0,0 127,0 => 127
0,0 127,127 => 254
0,127 127,0 => 254
0,127 127,127 => 127
127,0 127,127 => 255
75,7 69,2 => 11
47,58 36,79 => 42
77,9 111,23 => 48
123,100 111,60 => 80
120,23 55,41 => 83
28,20 91,68 => 111
85,107 69,46 => 123
16,25 100,100 => 159
62,85 22,5 => 160
92,26 59,113 => 174
62,22 35,125 => 206

```

# Answer
## JavaScript (ES6), ~~84~~ 78 bytes

*Saved 6 bytes thanks to Neil*

```
(a,b,c,d,x=a>c?a-c:c-a,y=b>d?b-d:d-b,z=x>y?x:y)=>y+z+(x+z&1?a+b+(b>d)&1||-1:0)

```

### Test cases

```
let f =

(a,b,c,d,x=a>c?a-c:c-a,y=b>d?b-d:d-b,z=x>y?x:y)=>y+z+(x+z&1?a+b+(b>d)&1||-1:0)

console.log(f(  1,  2,  1,  2)); // => 0
console.log(f(  0,  1,  1,  1)); // => 1
console.log(f(  1,  0,  1,  1)); // => 3
console.log(f(  2,  1,  5,  2)); // => 4
console.log(f(  0,  0,  0,127)); // => 253
console.log(f(  0,  0,127,  0)); // => 127
console.log(f(  0,  0,127,127)); // => 254
console.log(f(  0,127,127,  0)); // => 254
console.log(f(  0,127,127,127)); // => 127
console.log(f(127,  0,127,127)); // => 255
console.log(f( 75,  7, 69,  2)); // => 11
console.log(f( 47, 58, 36, 79)); // => 42
console.log(f( 77,  9,111, 23)); // => 48
console.log(f(123,100,111, 60)); // => 80
console.log(f(120, 23, 55, 41)); // => 83
console.log(f( 28, 20, 91, 68)); // => 111
console.log(f( 85,107, 69, 46)); // => 123
console.log(f( 16, 25,100,100)); // => 159
console.log(f( 62, 85, 22,  5)); // => 160
console.log(f( 92, 26, 59,113)); // => 174
console.log(f( 62, 22, 35,125)); // => 206
```

## Initial recursive solution, ~~100~~ ~~88~~ 81

*Saved 12 bytes thanks to ETHproductions*
*Saved 7 bytes thanks to Neil*

```
f=(a,b,c,d,e=b==d|a+b+(b>d)&1)=>a-c|b-d&&f(e?a+1-2*(a>c):a,e?b:b+1-2*(b>d),c,d)+1

```

### How it works

Although it still essentially applies to the current version, the following explanation more specifically refers to the initial version:

```
f=(a,b,c,d)=>b-d?a+b+(b>d)&1?f(a+1-2*(a>c),b,c,d)+1:f(a,b+1-2*(b>d),c,d)+1:Math.abs(a-c)

```

Going from ***(x0, y)*** to ***(x1, y)*** is trivial because we can go across lateral edges all the way from the source triangle to the target one. The Manhattan distance in this case is ***| x0 - x1 |***.

The tricky part is the vertical steps. To go from row ***y0*** to row ***y1***, we have to take these two parameters into account:

* The orientation of the current triangle
* Whether ***y0*** is less or greater than ***y1***

The orientation of a triangle is given by the parity of ***x + y***:

* if it's even, the triangle is up-pointing
* if it's odd, the triangle is down-pointing

We can go downwards from an up-pointing triangle (useful when ***y0 \< y1***) and upwards from a down-pointing triangle (useful when ***y0 \> y1***).

By combining the orientation of the triangle with the comparison between ***y0*** and ***y1***, we get the formula ***x + y0 + (y0 \> y1 ? 1 : 0)*** whose result is even if we can go in the desired direction and odd if not.

If we can't reach the next row directly, we first need to get a correct alignment by updating ***x***:

* if ***x*** is not yet equal to ***x1***, we definitely want to move in the correct direction, so we increment it if **x** is less than ***x1*** and we decrement it if ***x*** is greater than ***x1***
* if ***x*** already equals ***x1***, we can either increment or decrement it

### Test cases

```
f=(a,b,c,d)=>b-d?a+b+(b>d)&1?f(a+1-2*(a>c),b,c,d)+1:f(a,b+1-2*(b>d),c,d)+1:Math.abs(a-c)

console.log(f(  1,  2,  1,  2)); // => 0
console.log(f(  0,  1,  1,  1)); // => 1
console.log(f(  1,  0,  1,  1)); // => 3
console.log(f(  2,  1,  5,  2)); // => 4
console.log(f(  0,  0,  0,127)); // => 253
console.log(f(  0,  0,127,  0)); // => 127
console.log(f(  0,  0,127,127)); // => 254
console.log(f(  0,127,127,  0)); // => 254
console.log(f(  0,127,127,127)); // => 127
console.log(f(127,  0,127,127)); // => 255
console.log(f( 75,  7, 69,  2)); // => 11
console.log(f( 47, 58, 36, 79)); // => 42
console.log(f( 77,  9,111, 23)); // => 48
console.log(f(123,100,111, 60)); // => 80
console.log(f(120, 23, 55, 41)); // => 83
console.log(f( 28, 20, 91, 68)); // => 111
console.log(f( 85,107, 69, 46)); // => 123
console.log(f( 16, 25,100,100)); // => 159
console.log(f( 62, 85, 22,  5)); // => 160
console.log(f( 92, 26, 59,113)); // => 174
console.log(f( 62, 22, 35,125)); // => 206
```
> 7 votes

# Answer
# Python 2, 74 bytes

```
lambda x,y,X,Y:abs(y-Y)+max(x-X,X-x,abs(y-Y)+((x+y+X+Y)%-2)**(x^y^(Y>=y)))

```
> 5 votes

# Answer
## Batch, 99 bytes

```
@cmd/cset/a"x=%3-%1,x*=x>>31|1,y=%4-%2,w=y>>31,y*=w|1,z=x+(y+x&1)*(-(%1+%2+w&1)|1)-y,z*=z>>31,x+y+z

```

Explanation: A horizonal-only motion simply takes the absolute x-coordinate difference. For large enough x, the vertical motion only takes one extra step per absolute y-coordinate difference, but for small x, it takes four extra steps per two y-coordinate difference, plus one or three steps for an odd difference. This is calcluated as two steps per difference plus a correction factor. The larger of the corrected two steps and the sum of absolute differences is then the result, although this is itself calculated as the larger of the corrected absolute y-coordinate difference and the absolute x-coordinate distance added to the uncorrected absolute y-coordinate difference.

* `@cmd/cset/a"` \- Evaluates comma-separated expressions and prints the last one
* `x=%3-%1,x*=x>>31|1` Calculates \\$x=|x\_2-x\_1|\\$
* `y=%4-%2,w=y>>31,y*=w|1` Calculates \\$w=y\_1\>y\_2\\$ and \\$y=|y\_2-y\_1|\\$
* `z=x+(y+x&1)*(-(%1+%2+w&1)|1)-y` Correction factor \\$c=(y+(x\bmod2))(1-2((x\_1+y\_1+w)\bmod2)), z=x+c-y\\$
* `z*=z>>31,x+y+z` Calculates \\$max(x,y-c)+y = x+y-min(0,x+c-y)\\$
> 2 votes

# Answer
# 05AB1E, 24 bytes

Port of my Pyth answer, which in turn uses roughly the same approach as feersum's Python answer. Takes input as a list of pairs of coordinates, \\$(x\_1, x\_2), (y\_1, y\_2)\\$. Fixed a bug for +1 byte, then fixed another mistake for +1, but which yielded the correct result for all test cases...

```
ÆÄ`©I˜OÉ(IøнOIθD{Q+m+M®+

```

Try it online!

### Breakdown

```
ÆÄ`©I˜OÉ(IøнOIθD{Q+m+M®+    Full program. I represents the evaluated input.
ÆÄ                          Reduce the pairs by subtraction, take the absolute values.
  `©                        Dump them separately onto the stack and store the second
                            one, |y1-y2| in the register C.
    I˜O                     Push the sum of flattened input onto the stack.
       É(                   Take its parity and negate it.
         Iøн                Push [x1, y1].
            O               Take x1+y1 (sum them).
             IθD{Q          Then check if the second pair is sorted (y1 ≤ y2).
                  +         And sum that with x1+y1.
                   m        Exponentiate. Push the parity above ** the result.
                    +       And add the second absolute difference to that.
                     M®+    As a result, push the largest number on the stack
                            plus the value stored in register C.

```
> 2 votes

# Answer
# Jelly, 24 bytes

```
⁴<³¬Ḋ;³S
SḂN*¢+ḊḤ$
ạµS»Ç

```

Try it online!

Let’s call the input \\$(x,y),(X,Y)\\$. I worked from feersum's formula:

\begin{align\*} d &= |y-Y|+\max(|x-X|,|y-Y|+((x+y+X+Y)\bmod -2)^{x \oplus y \oplus (Y \geq y)}) \\ &= |y-Y|+\max(|x-X|,|y-Y|+\[-(|x-X|+|y-Y| \bmod 2)\]^{x + y + (Y \not\< y)}) \\ &= \max(|x-X|+|y-Y|,2|y-Y|+\[-(|x-X|+|y-Y| \bmod 2)\]^{(Y \not\< y) + x + y}). \end{align\*}

The first line computes \\$¢ = (Y \not\< y)+x+y\\$, the exponent in the formula.

The last line first computes \\$L=\[|x-X|, |y-Y|\]\\$ and then computes the maximum of \\$\text{sum}(L)\\$ and \\$f(L)\\$, where \\$f\\$ is the function on the middle line.

The middle line, given \\$L=\[a, b\]\\$, computes \\$-((a+b)\bmod 2)\\$, takes that to the \\$¢\\$'th power, then adds \\$2b\\$.
> 2 votes

# Answer
# Python 2, ~~74~~ ~~72~~ 71 bytes

```
lambda c,a,d,b:abs(a-b)+abs(a+(-c-a)/2-b-(-d-b)/2)+abs((c+a)/2-(d+b)/2)

```

Try it online! Link includes test cases. Edit: Saved 2 bytes thanks to @JoKing. Saved a further byte thanks to @Mr.Xcoder. Based on the following formula I found in this question:

$$ \left|a\_i-b\_i\right|\\;+\\;\left|\left(a\_i-\left\lfloor\frac{a\_j}2\right\rfloor\right)-\left(b\_i-\left\lfloor\frac{b\_j}2\right\rfloor\right)\right|\\;+\\;\left|\left\lfloor\frac{a\_j+1}2\right\rfloor-\left\lfloor\frac{b\_j+1}2\right\rfloor\right| $$

The coordinate systems differ in three ways; the coordinates are exchanged (which explains my somewhat strange parameter name order), the coordinates are angled at \\$120^\circ\\$ rather than \\$90^\circ\\$ (which explains the two additions) and the coordinates in the linked question use inferior 1-indexing. Since we're taking differences this cancels out most of the time and we are left with:

$$ \left|a\_i-b\_i\right|\\;+\\;\left|\left(a\_i-\left\lfloor\frac{a\_j+1}2\right\rfloor\right)-\left(b\_i-\left\lfloor\frac{b\_j+1}2\right\rfloor\right)\right|\\;+\\;\left|\left\lfloor\frac{a\_j}2\right\rfloor-\left\lfloor\frac{b\_j}2\right\rfloor\right| $$

This can then be golfed by noting that \\$ -\lfloor\frac{a\_j+1}2\rfloor = \lfloor\frac{-a\_j}2\rfloor \\$.
> 2 votes

# Answer
# racket/scheme, 214 bytes

```
(define(f x y X Y)(let m((p x)(q y)(c 0))
(let((k(+ c 1))(d(- Y q)))
(cond((= 0(- X p)d)c)
((and(> d 0)(even?(+ p q)))(m p(+ q 1)k))
((and(< d 0)(odd?(+ p q)))(m p(- q 1)k))
((< p X)(m(+ p 1)q k))
(else(m(- p 1)q k))))))

```
> 2 votes

# Answer
# Java 8, ~~157~~ ~~190~~ ~~188~~ ~~144~~ ~~142~~ ~~141~~ ~~127~~ 125 bytes

```
(a,b,x,y)->{int r=0,c=1,z=1;for(;(c|z)!=0;r--)if((z=y-b)*z<(c=x-a)*c|a%2!=b%2?z<0:z>0)b+=z<0?-1:1;else a+=c<0?-1:1;return~r;}

```

+33 bytes (157 → 190) due to a bug fix.
-44 bytes (188 → 144) converting the recursive method to a single looping method.
-16 bytes thanks to *@ceilingcat*.

**Explanation:**

Try it here.

```
(a,b,x,y)->{        // Method with four integers as parameter and integer return-type
                    // (a=x1; b=y1; x=x2; y=y2)
  int r=0,          //  Result-integer `r`, starting at 0
      c=1,z=1;      //  Temp integers for the differences, starting at 1 for now
  for(;(c|z)!=0;    //  Loop until both differences are 0
      r--){         //    After every iteration: decrease the result `r` by 1
    if((z=y-b)      //   Set `z` to y2 minus y1
       *z<          //   And if its squared value is smaller than:
          (c=x-a)   //    Set `c` to x2 minus x1
                 *c //    And square it as well
       |a%2!=b%2?   //   OR if the triangle at the current location is facing downwards
         z<0        //       and we have to go upwards,
        :z>0)       //      or it's facing upwards and we have to go downwards:
      b+=z<0?-1:1;  //    In/decrease y1 by 1 depending on where we have to go
    else            //   Else:
     a+=c<0?-1:1;}  //    In/decrease x1 by 1 depending on where we have to go
  return~r;         //  Return `-r-1` as result

```
> 1 votes

# Answer
# Pyth, ~~31~~ 28 bytes

Uses roughly the same approach as in feersum's Python answer. Takes input as a list of pairs of coordinates, \\$(x\_1, x\_2), (y\_1, y\_2)\\$. Fixed a bug for -1 byte.

```
+eKaMQg#hK+eK^%ssQ_2+shCQSIe

```

Try it here! or Try the test suite!

## Breakdown

```
+eKaMQg#hK+eK^%ssQ_2xxFhCQSIe     Full program. Q = eval(input()).
  KaMQ                            Store the differences [|x1-x2|, |y1-y2|] in K.
 e                                Retrieve the latter (|y1-y2|).
+     g#                          And add it to the greatest value between:
        hK                          - The head of K (|x1-x2|)
          +                         - And the result of adding:
           eK                           The end of K (|y1-y2|).
             ^                      - with the result of exponentiating:
              %ssQ_2                    The sum of the flattened Q, modulo -2.
                                        Yields -1 if x1+x2+y1+y2 is odd, 0 otherwise.
                    xxFhCQSIe       - by the result of this expression:
                       hCQ              Transpose Q and get the head (x1, y1).
                     xF                 Reduce by bitwise XOR.
                          SIe           And check if the list [y1, y2] is sorted.
                    x                   After which, xor the result by the bool (0/1).

```
> 1 votes

# Answer
# Jelly, ~~22 .. 16~~ 15 bytes

```
ṭH,‘H_ɗɗ@+¥ḞIAS

```

Try it online!

Try all test cases.

Uses @Neil's method in this answer which uses a modified formula from this math.SE question.

Takes the coordinates as arguments `y1, y2` and `x1, x2`.
> 1 votes

# Answer
# 05AB1E, 16 bytes

Uses a modified version of Neil's answer, optimised for stack-based languages like 05AB1E. Takes input as two pairs of coordinates, \\$(x\_1, x\_2), (y\_1, y\_2)\\$, separated by a newline from STDIN. Initially I merged that with my other 05AB1E answer but then decided to post it separately because it's very, very different.

```
+D(‚2÷Æ`²Æ©+®)ÄO

```

Try it online! or Try the test suite! (Uses a slightly modified version of the code (`®` instead of `²`), courtesy of Kevin Cruijssen)
> 1 votes

---
Tags: code-golf, geometry, path-finding, hexagonal-grid
---


# Question
Title: Create a hash from an array of arrays of key|value pairs
Starting from an array of sub-arrays, each sub-array is a 2 elements key|value pair. Example

```
input = [["A", 5], ["B", 2], ["A", 12], ["B", -2], ["C", 7]]

```

Convert this into a hash that has distinct keys from above, with values summed up, so the above would be:

```
output = {"A" => 17, "B" => 0, "C" => 7}

```

# Answer
## Perl

Hopefully this thread won't get tagged code-golf

```
use strict;
use warnings;

my $a = [["A", 5], ["B", 2], ["A", 12], ["B", -2], ["C", 7]];

my %h = ();

map { $h{shift @$_} += shift @$_ } @$a;

while (my ($k, $v) = each %h)
{
  print "$k = $v $/";
}

```

Output:

```
$ perl cg-arraysum.pl
A = 17
C = 7
B = 0

```
> 2 votes

# Answer
## Ruby

```
input.each_with_object(Hash.new(0)) { |(k, v), h| h[k] += v }

```

(see http://api.rubyonrails.org/classes/Enumerable.html#method-i-each\_with\_object)
> 2 votes

# Answer
# k

This can probably be shortened from 17 chars but:

```
{+/'(*|+x)(=*+x)}

```

Example:

```
k)x:(("A";5);("B";2);("A";12);("B";-2);("C";7))
k){+/'(*|+x)(=*+x)}x
"ABC"!17 0 7

```

or with output in q output format:

```
A| 17
B| 0
C| 7

```
> 1 votes

# Answer
## There's more than one way to do it (Perl)

Preparation:

```
$input = [["A", 5], ["B", 2], ["A", 12], ["B", -2], ["C", 7]];

```

I have three Perl snippets to solve the problem because the obvious code is kinda boring. It looks like this:

```
$output1{$_->[0]} += $_->[1] for @$input;

```

I'm a fan of list expressions, so here's a single list expression, I had to use map and his friend grep a lot:

```
%output2 = map {
    $c = $_ => sum map $_->[1] => grep $_->[0] eq $c => @$input
} keys %{{map @$_ => @$input}};

```

Many years I tried to find a non-trivial use-case for List::Utils **reduce** function which folds list elements with a given function, and finally here it is. A second single list expression, btw. \o/

```
$output3 = reduce {
    $a = {@$a} if ref $a eq 'ARRAY'; $a->{$b->[0]} += $b->[1]; $a;
} @$input;

```
> 1 votes

# Answer
## C

Assumes keys are 'A'..'Z', assumes values are numbers, assumes input format is exactly spaced/formatted as specified in example.

```
char*p;c[99],m[99];
main(int i,char**v){
    for(p=v[1]+1;*p==91;){
        c[p[2]]+=atoi(p+5);
        m[p[2]]=1;
        for(;*p-93;p++);
        p+=p[1]-44?1:3;
    }
    printf("{");
    for(p="";++i<99;)if(m[i])printf("%s\"%c\" => %d",p,i,c[i]),p=", ";
    printf("}\n");
}

```

Run as:

```
./a.out '[["A", 5], ["B", 2], ["A", 12], ["B", -2], ["C", 7]]'

```

Output:

```
{"A" => 17, "B" => 0, "C" => 7}

```
> 1 votes

# Answer
# R - 20 chars

```
aggregate(.~k,x,sum)

```

where `x` is the input defined here:

```
x <- data.frame(k = c("A", "B", "A", "B", "C"),
                v = c(5, 2, 12, -2, 7))
x
#     k     v
# 1   A     5
# 2   B     2
# 3   A    12
# 4   B    -2
# 5   C     7

```

The output:

```
aggregate(.~k,x,sum)
#   k  v
# 1 A 17
# 2 B  0
# 3 C  7

```
> 0 votes

# Answer
### Python

```
output = {};
for x,y in input:
    output[x] = y + ( output.get(x) or 0 )

```

Thanks for @Daniero's comment.
> 0 votes

# Answer
# Mathematica

In Mathematica the input array would be represented as `n = {{"A", 5}, {"B", 2}, {"A", 12}, {"B", -2}, {"C", 7}};`.

**Code**

```
{#[[1, 1]], Total@##[[All, 2]]} & /@ GatherBy[n, First]

```

> {{"A", 17}, {"B", 0}, {"C", 7}}

---

**Explanation**

`GatherBy[n, First]` gathers the data by the first element in each subarray (sublist).

> {{{"A", 5}, {"A", 12}}, {{"B", 2}, {"B", -2}}, {{"C", 7}}}

`Total@##[[All, 2]]` sums the second elements for each string, `#[[1, 1]]` (in this case, for "A", "B", and "C").
> 0 votes

# Answer
# Python 52 43

```
{X:sum(y for Y,y in a if Y==X)for X,x in a}

```

The input array is in `a`

This is fairly inefficient because it calculates the sum of all the 'A' keys for every occurrence of the 'A' key, but it works and since there aren't really any winning criteria I decided to golf my answer as much as possible.

**Edit:** Removed some unnecessary brackets and indices per recommendation from Daniero
> 0 votes

# Answer
# ruby, 40 (or 38)

```
h=Hash.new(0);input.map{|k,v|h[k]+=v}
h

```

`h` contains the result. The 38-char solution creates the wanted result, the 40 characters solution returns the result.

The result as complete testcode:

```
input = [["A", 5], ["B", 2], ["A", 12], ["B", -2], ["C", 7]]
output = {"A" => 17, "B" => 0, "C" => 7}

h=Hash.new(0);input.map{|k,v|h[k]+=v}
p h == output #true

```

A little variant (but with the same size):

```
h={};input.map{|k,v|h[k]=(h[k]||0)+v}

```
> 0 votes

# Answer
# TXR

```
@(do (let ((h (hash :equal-based)))
       (each ((entry '(("A" 5) ("B" 2) ("A" 12) ("B" -2) ("C" 7))))
         (inc [h (first entry) 0] (second entry)))
       (format t "~a\n" h)))

```

Run:

```
$ txr sumhash.txr
#H((:equal-based) ("A" 17) ("B" 0) ("C" 7))

```

What if we take the question literally? The question says that the input is this:

```
input = [["A", 5], ["B", 2], ["A", 12], ["B", -2], ["C", 7]]

```

and that the output looks like this:

```
output = {"A" => 17, "B" => 0, "C" => 7}

```

Fair enough. We can make a program to transform that exact input to that exact output, rather than massaging the data to fit the syntax of our programming language.

```
input = [@(coll :vars (key value))["@key", @{value /[+\-]?[0-9]+/}]@(last)]@(end)
@(bind (keyout valout) @(let ((h (hash :equal-based)))
                          (each* ((k key)
                                  (v [mapcar (op int-str @1 10) value]))
                             (inc [h k 0] v))
                          '(,(hash-keys h) ,(hash-values h))))
@(output)
output = {@(rep)"@keyout" => @valout, @(last)"@keyout" => @valout@(end)}
@(end)

```

Run:

```
$ txr sumhash2.txr -   # input read from stdin
input = [["A", 5], ["B", 2], ["A", 12], ["B", -2], ["C", 7]]
<user types Ctrl-D>
output = {"A" => 17, "B" => 0, "C" => 7}

```

Empty case:

```
$ txr sumhash2.txr -
input = []
<Ctrl-D>
output = []

```

Invalid input:

```
$ txr sumhash2.txr
foo
<Ctrl-D>
false

```

Word `false` is output, termination status is failed.

Simplified version in which hashing is worked into data acquisition (side effects during pattern matching):

```
@(bind h @(hash :equal-based))
input = [@(coll)["@key", @{value /[+\-]?[0-9]+/}]@\
                @(do (inc [h key 0] (int-str value 10)))@\
                @(last)]@(end)
@(bind (keyout valout) (@(hash-keys h) @(hash-values h)))
@(output)
output = {@(rep)"@keyout" => @valout, @(last)"@keyout" => @valout@(end)}
@(end)

```
> 0 votes

# Answer
## Javascript 79

```
function f(a){r={};for(e in a){i=a[e][0];j=a[e][1];r[i]=r[i]?r[i]+j:j}return r}

```

Test : `f([["A", 5], ["B", 2], ["A", 12], ["B", -2], ["C", 7]])`
Outputs : `Object {A: 17, B: 0, C: 7}`
> 0 votes

# Answer
# Swift 4

**Optimized for clarity, not character count.** Replace the input as needed or pass it in some other way (e.g. through an I/O class).

```
import Darwin // or Glibc if on Linux

let input = [["A", 5], ["B", 2], ["A", 12], ["B", -2], ["C", 7]]

var output: [String: Int] = [:]

for pair in input {
    guard let key = pair[0] as? String else {
        fputs("Input error: key '\(pair[0])' is not a string\n", __stderrp)
        exit(1)
    }
    if output[key] == nil { // the first of this key
        output[key] = 0 // have to add to something, so put in 0
    }
    output[key]! += pair[1] as? Int ?? 0
}

for pair in output.sorted(by: <) {
    print("\(pair.key): \(pair.value)")
}

```

---

### Bonus!

* Error handling:
    + If you input keys that aren't strings, it will give an error message and exit.
    + If you input values that aren't integers, it will silently assume they're 0.
* The output is sorted.
> 0 votes

# Answer
# Java 8

Here's an approach that uses streams:

```
import java.util.Map;
import java.util.HashMap;
import java.util.stream.Stream;
import java.util.stream.Collectors;

```

```
public static Map<?, Integer> collect(Object[][] pairs) {
    return Stream.of(pairs)
        .collect(Collectors.groupingBy(
            pair -> pair[0],
            HashMap::new,
            Collectors.reducing(
                0,
                pair -> (Integer) pair[1],
                Integer::sum
            )
        ))
    ;
}

```

Uses a collector that groups elements into a map based on their first element, with duplicate values reduced by addition.

Or there's the classic loop version:

```
public static Map<?, Integer> collect(Object[][] pairs) {
    Map<Object, Integer> map = new HashMap<>();
    for (Object[] pair : pairs) {
        Object k = pair[0];
        int v = (Integer) pair[1];
        map.put(k, map.getOrDefault(k, 0) + v);
    }
    return map;
}

```
> 0 votes

# Answer
# Java - 71

```
<T> Map m(final T[][] i) {
  return new HashMap() {
    { for (T[] p : i) put(p[0], p[1]); }
  };
}

```

The following...

```
System.out.println(m(new Object[][] {
  { "A", 5 }, { "B", 2 }, { "A", 12 }, { "B", -2 }, { "C", 7 }
}));

```

produces:

> `{A=12, B=-2, C=7}`
> -1 votes

---
Tags: code-challenge, array
---