15
\$\begingroup\$

Given a list of chords label them as either 'Major' or ' Minor'.

Input

Input will be a list of chords, one per line, made up of 3 notes separated by a space. Each note will consist of the note name in uppercase (A-G) and an optional accidental (# or b). Chords may be in any inversion (i.e. the notes may be in any order).

Output

If the chord is major, output 'Major'. If the chord is minor, output 'Minor'. If the chord is neither major nor minor, output a blank line.

Example

Input

C E G
F Ab C
C Eb Gb
E G B
Db F Ab
Bb G D
D A Gb

Output

Major
Minor

Minor
Major
Minor
Major

Test scripts

As in some of my past questions, I've once again butchered some test scripts originally created by Joey and Ventero to provide some test cases for this question:

Usage: ./test [your program and its arguments]

Rewards

Each entry which I can verify that meets the spec, passes the tests and has obviously had some attempt at golfing will receive an upvote from me (so please provide usage instructions with your answer). The shortest solution by the end of 13/10/2012 will be accepted as the winner.

A little theory

For those of you with no musical theory knowledge here's enough information for you to be able to compete.

A major or minor chord is made up of three notes which are separated by a specific pattern of semitones. If we consider the root (bottom note) of the chord to be 0, then a major chord is the pattern 0-4-7 and a minor chord is the pattern 0-3-7. Things are made more awkward by the fact that some notes are a semitone apart and some are a tone apart. The spread of semitones from Ab-G# is as follows:

G#/Ab A A#/Bb B/Cb B#/C C#/Db D D#/Eb E/Fb E#/F F#/Gb G G#/Ab
  0   1   2    3     4    5   6   7    8     9    10  11  12

G#/Ab means that that G# is the same note as Ab. From this we can see that the chord Ab C Eb is a major chord, and that Ab Cb Eb is minor.

To complicate matters further, the chord Eb Cb Ab is considered to be the same as Ab Cb Eb, Cb Eb Ab and Cb Ab Eb and so on. Every one of these variations is still a minor chord.

\$\endgroup\$
  • 2
    \$\begingroup\$ I think your bash tester needs inputs and expected answers swapped. \$\endgroup\$ – flodel Sep 29 '12 at 11:32
  • \$\begingroup\$ @flodel Yes, you're right. Sorry about that, I've corrected it now. I'll need to check that the Powershell test script doesn't have the same problem too. \$\endgroup\$ – Gareth Sep 29 '12 at 13:04
3
\$\begingroup\$

GolfScript, 83 chars

n%{' '%{1\{'A#BC D EF G'?+}/.}%$(+2/{~- 12%}%.(+.(+]$0=.$]10,7>?'MMaijnoorr

'>2%}%

This is a quick first solution; I'm sure this can be golfed further. Passes the bash test suite, after fixing the bug pointed out by flodel in the comments.

Edit 1: Saved 5 chars with a shorter way to recognize the canonicalized major and minor chord patterns.

Edit 2: Saved 2 more chars with more compact output encoding inspired by grc's solution. (Thanks!) As a side effect, the code now prints an extra blank line after the output, but the test harness seems to accept that, so I guess it's OK. :)

Here's how it works:

  • The outer loop n%{ }%n* just splits the input into lines, runs the code inside the braces for each line and joins the results with newlines.

  • ' '% splits each line into an array of notes. For each of those notes, 1\{'A#BC D EF G'?+}/ then converts that note into a semitone number by searching for each of its characters in the string 'A#BC D EF G' and adding up the positions (which will be -1 for any character not found in the string, notably including b). (I'm sure I've seen this trick used before.) Finally, the . duplicates each number, so that, at the end of the loop, e.g. the input F Ab C has been turned into [9 9 0 0 4 4].

  • We then sort the notes with $, move the first note to the end with (+, and split the array into pairs with 2/, so that it now looks e.g. like [[9 0] [0 4] [4 9]]. Then {~- 12%}% maps each pair of notes into its difference modulo 12, turning our example array into [9 8 7].

  • Next, .(+ makes a copy of the array and rotates its elements left by one position. We do this twice and collect the copies into an array with ], so that our example now looks like [[9 8 7] [8 7 9] [7 9 8]].

  • We then sort this array of arrays with $ and take the first element — in this case [7 9 8] — with 0=. We then make a copy of this array (.), sort it ($), collect both the unsorted and the unsorted array into another array of arrays (]), and search for the first occurrence of the array [7 8 9] (which is written as 10,7> to save two chars) in it.

  • This gives us either 0 (if the unsorted array was [7 8 9], and thus the chord is major), 1 (if the unsorted array was a permutation of [7 8 9], which, given that its first element must be smallest, can only be [7 9 8], making the chord minor) or -1 (if even the sorted array does not equal [7 8 9]).

  • This number is then used as a starting index into the string "MMaijnoorr\n\n" (where the \ns are given as actual linefeeds in the code), from which we take that character and every second subsequent one as the output. If the index is -1, we start from the last character of the string, which is just a line feed.

\$\endgroup\$
  • \$\begingroup\$ Nice explanation. I'm having the same difficulty I always seem to have with GolfScript - I can invoke it a line at a time to test with echo "G# B# Eb" | ruby golfscript.rb ilmari.gs but how do I run the test script on it? I tried ./test ruby golfscript.rb ilmari.gs but it wasn't having any of it. (I've already given you a +1 because it obviously works, but I'm just curious) \$\endgroup\$ – Gareth Sep 30 '12 at 7:43
  • 1
    \$\begingroup\$ @Gareth: Looks like that's a bug in the bash test script -- it doesn't handle multiple arguments right. To fix it, replace args="$@" with args=("$@") and got=$("$cmd" "$args") with got=$("$cmd" "${args[@]}"). (Or just make golfscript.rb executable and run it as ./test ./golfscript.rb chords.gs.) \$\endgroup\$ – Ilmari Karonen Sep 30 '12 at 9:42
4
\$\begingroup\$

Python, 160

f='A#BC D EF G3453543'.find
try:
 while 1:x,y,z=sorted(map(lambda x:f(x[0])+f(x[1:])+1,raw_input().split()));print'MMianjoorrr'[f(`y-x`+`z-y`)/14:-1:2]
except:0
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.