Skip to content

Instantly share code, notes, and snippets.

@ygrenzinger
Created April 28, 2020 19:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ygrenzinger/df5e1e9cdc341ed6d16f663499d130c2 to your computer and use it in GitHub Desktop.
Save ygrenzinger/df5e1e9cdc341ed6d16f663499d130c2 to your computer and use it in GitHub Desktop.
Unit Converter exercise on Jetbrain academy
Enter what you want to convert (or exit): 1 degree Celsius to Kelvins
1.0 degree Celsius is 274.15 Kelvins
Enter what you want to convert (or exit): -272.15 dc to K
-272.15 degrees Celsius is 1.0 Kelvin
Enter what you want to convert (or exit): 1 kn to feet
Conversion from ??? to feet is impossible
Enter what you want to convert (or exit): 1 km to feet
1.0 kilometer is 3280.839895013123 feet
Enter what you want to convert (or exit): 3 pount to ounces
Conversion from ??? to ounces is impossible
Enter what you want to convert (or exit): 3 pound to ounces
3.0 pounds is 47.99999999999999 ounces
Enter what you want to convert (or exit): 3 kelvins to grams
Conversion from Kelvins to grams is impossible
Enter what you want to convert (or exit): 3 grams to meters
Conversion from grams to meters is impossible
Enter what you want to convert (or exit): exit
Enter what you want to convert (or exit): 1 F in K
1.0 degree Fahrenheit is 255.92777777777778 Kelvins
Enter what you want to convert (or exit): 1 K in F
1.0 Kelvin is -457.87 degrees Fahrenheit
Enter what you want to convert (or exit): 1 C in K
1.0 degree Celsius is 274.15 Kelvins
Enter what you want to convert (or exit): 1 K in C
1.0 Kelvin is -272.15 degrees Celsius
Enter what you want to convert (or exit): 1 F in C
1.0 degree Fahrenheit is -17.22222222222222 degrees Celsius
Enter what you want to convert (or exit): 1 C in F
1.0 degree Celsius is 33.8 degrees Fahrenheit
Enter what you want to convert (or exit): one boa in parrots
Parse error
Enter what you want to convert (or exit): please convert distance to the Moon to steps
Parse error
Enter what you want to convert (or exit): many things to improve!
Parse error
Enter what you want to convert (or exit): exit
package converter
import java.util.*
interface Unit {
fun label(): String
fun type(): String
fun <T : Unit> conversionMessage(x: Double, destination: T): String
fun conversionMessage(x: Double, destination: String): String {
return findUnit(destination)?.let {
return if (this.type() == it.type()) {
conversionMessage(x, it)
} else {
"Conversion from ${this.label()} to ${it.label()} is impossible"
}
} ?: "Conversion from ${this.label()} to ??? is impossible"
}
companion object {
fun findUnit(unit: String): Unit? = UnitOfLength.toUnit[unit] as Unit?
?: UnitOfWeight.toUnit[unit] as Unit?
?: UnitOfTemperature.toUnit[unit] as Unit?
fun conversionMessage(x: Double, origin: String, destination: String): String {
return findUnit(origin)?.let {
return it.conversionMessage(x, destination)
} ?: findUnit(destination)?.let {
"Conversion from ??? to ${it.label()} is impossible"
} ?: "Conversion from ??? to ??? is impossible"
}
}
}
interface SimpleUnit : Unit {
val abbr: String
val singular: String
val plural: String
val ratio: Double
fun labelFor(x: Double) = if (x == 1.0) {
singular
} else {
plural
}
override fun label() = plural
override fun <T : Unit> conversionMessage(x: Double, destination: T): String {
return this.conversionMessage(x, destination as SimpleUnit)
}
fun conversionMessage(x: Double, destination: SimpleUnit): String {
if (x < 0) return "${this.type()} shouldn't be negative."
val c = (x * this.ratio) / destination.ratio
return "$x ${this.labelFor(x)} is $c ${destination.labelFor(c)}"
}
}
enum class UnitOfLength(override val abbr: String, override val singular: String, override val plural: String, override val ratio: Double) : SimpleUnit {
METER("m", "meter", "meters", 1.0),
KILOMETER("km", "kilometer", "kilometers", 1000.0),
CENTIMETER("cm", "centimeter", "centimeters", 0.01),
MILLIMETER("mm", "millimeter", "millimeters", 0.001),
MILE("mi", "mile", "miles", 1609.35),
YARD("yd", "yard", "yards", 0.9144),
FOOT("ft", "foot", "feet", 0.3048),
INCH("in", "inch", "inches", 0.0254);
override fun type() = "Length"
companion object {
val toUnit: Map<String, UnitOfLength> = values()
.flatMap { u -> listOf(u.abbr to u, u.singular to u, u.plural to u) }
.toMap()
}
}
enum class UnitOfWeight(override val abbr: String, override val singular: String, override val plural: String, override val ratio: Double) : SimpleUnit {
GRAM("g", "gram", "grams", 1.0),
KILOGRAM("kg", "kilogram", "kilograms", 1000.0),
MILLIGRAM("mg", "milligram", "milligrams", 0.001),
POUND("lb", "pound", "pounds", 453.592),
OUNCE("oz", "ounce", "ounces", 28.3495);
override fun type() = "Weight"
override fun labelFor(x: Double) = if (x == 1.0) {
singular
} else {
plural
}
companion object {
val toUnit: Map<String, UnitOfWeight> = values()
.flatMap { u -> listOf(u.abbr to u, u.singular to u, u.plural to u) }
.toMap()
}
}
enum class UnitOfTemperature(val names: List<String>, val convert: (Double, UnitOfTemperature) -> Double) : Unit {
CELSIUS(listOf("degree Celsius", "degrees Celsius", "celsius", "dc", "c"),
{ x, unit ->
when (unit) {
CELSIUS -> x
FAHRENHEIT -> (x * 9.0) / 5.0 + 32.0
KELVINS -> x + 273.15
}
}) {
override fun label() = "degrees Celsius"
},
FAHRENHEIT(listOf("degree Fahrenheit", "degrees Fahrenheit", "fahrenheit", "df", "f"),
{ x, unit ->
when (unit) {
CELSIUS -> (x - 32.0) * (5.0 / 9.0)
FAHRENHEIT -> x
KELVINS -> (x + 459.67) * (5.0 / 9.0)
}
}) {
override fun label() = "degrees Fahrenheit"
},
KELVINS(listOf("Kelvin", "Kelvins", "k"),
{ x, unit ->
when (unit) {
CELSIUS -> x - 273.15
FAHRENHEIT -> (x * 9.0) / 5.0 - 459.67
KELVINS -> x
}
}) {
override fun label() = "Kelvins"
};
override fun type() = "Temperature"
private fun labelFor(x: Double) = if (x == 1.0) {
names[0]
} else {
names[1]
}
override fun <T : Unit> conversionMessage(x: Double, destination: T): String {
return this.conversionMessage(x, destination as UnitOfTemperature)
}
private fun conversionMessage(x: Double, destination: UnitOfTemperature): String {
val c = this.convert(x, destination)
return "$x ${labelFor(x)} is $c ${destination.labelFor(c)}"
}
companion object {
val toUnit: Map<String, UnitOfTemperature> = values()
.flatMap { u -> u.names.map { it.toLowerCase() to u } }
.toMap()
}
}
fun parseAndConvert(line: String): String {
val regex = """(-?[0-9]+(\.?[0-9]+)?)\s((degree\s|degrees\s)?[a-zA-Z]+)\s([a-zA-Z]+)\s(.+)""".toRegex()
val matchResult: MatchResult = regex.find(line) ?: return "parse error"
val value = matchResult.groupValues[1].toDouble()
val origin = matchResult.groupValues[3]
val destination = matchResult.groupValues[6]
return Unit.conversionMessage(value, origin, destination)
}
private fun promptUnitOfLength(scanner: Scanner) {
do {
print("Enter what you want to convert (or exit): ")
val line = scanner.nextLine()
if (line.isBlank()) {
continue
}
if (line == "exit") {
break
}
val result = parseAndConvert(line.toLowerCase())
println(result)
} while (true)
}
fun main() {
val scanner = Scanner(System.`in`)
promptUnitOfLength(scanner)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment