Initial commit
This commit is contained in:
commit
595820961e
19 changed files with 4921 additions and 0 deletions
3551
.editorconfig
Normal file
3551
.editorconfig
Normal file
File diff suppressed because it is too large
Load diff
583
.gitignore
vendored
Normal file
583
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,583 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/csharp,visualstudio
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=csharp,visualstudio
|
||||
|
||||
### Csharp ###
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.tlog
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||
*.vbp
|
||||
|
||||
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||
*.dsw
|
||||
*.dsp
|
||||
|
||||
# Visual Studio 6 technical files
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# Visual Studio History (VSHistory) files
|
||||
.vshistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
# VS Code files for those working on multiple tools
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Windows Installer files from build outputs
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
|
||||
### VisualStudio ###
|
||||
|
||||
# User-specific files
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
|
||||
# Mono auto generated files
|
||||
|
||||
# Build results
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
|
||||
# MSTest test Results
|
||||
|
||||
# NUnit
|
||||
|
||||
# Build Results of an ATL Project
|
||||
|
||||
# Benchmark Results
|
||||
|
||||
# .NET Core
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
|
||||
# StyleCop
|
||||
|
||||
# Files built by Visual Studio
|
||||
|
||||
# Chutzpah Test files
|
||||
|
||||
# Visual C++ cache files
|
||||
|
||||
# Visual Studio profiler
|
||||
|
||||
# Visual Studio Trace Files
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
|
||||
# TeamCity is a build add-in
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
|
||||
# Visual Studio code coverage results
|
||||
|
||||
# NCrunch
|
||||
|
||||
# MightyMoose
|
||||
|
||||
# Web workbench (sass)
|
||||
|
||||
# Installshield output folder
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
|
||||
# Click-Once directory
|
||||
|
||||
# Publish Web Output
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
|
||||
# NuGet Packages
|
||||
# NuGet Symbol Packages
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
# except build/, which is used as an MSBuild target.
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
|
||||
# Windows Store app package directories and files
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
# but keep track of directories ending in .cache
|
||||
|
||||
# Others
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
|
||||
# RIA/Silverlight projects
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
|
||||
# SQL Server files
|
||||
|
||||
# Business Intelligence projects
|
||||
|
||||
# Microsoft Fakes
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
|
||||
# Visual Studio 6 build log
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
|
||||
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||
|
||||
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||
|
||||
# Visual Studio 6 technical files
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
|
||||
# Paket dependency manager
|
||||
|
||||
# FAKE - F# Make
|
||||
|
||||
# CodeRush personal settings
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
|
||||
# BizTalk build output
|
||||
|
||||
# OpenCover UI analysis results
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
|
||||
# Local History for Visual Studio
|
||||
|
||||
# Visual Studio History (VSHistory) files
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
|
||||
# VS Code files for those working on multiple tools
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
|
||||
# Windows Installer files from build outputs
|
||||
|
||||
# JetBrains Rider
|
||||
|
||||
### VisualStudio Patch ###
|
||||
# Additional files built by Visual Studio
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/csharp,visualstudio
|
||||
155
Mosaic.Test/CompanyTests.cs
Normal file
155
Mosaic.Test/CompanyTests.cs
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
using System.Reflection;
|
||||
|
||||
namespace Mosaic.Test;
|
||||
|
||||
public sealed class CompanyTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public void Construction()
|
||||
{
|
||||
const string Name = "A";
|
||||
const decimal M2Price = 12.34M;
|
||||
const decimal HourlyWage = 56.67M;
|
||||
const int Margin = 6;
|
||||
Worker[] workers = CreateSampleWorkers(1);
|
||||
|
||||
var company = new Company(Name, M2Price, HourlyWage, Margin, workers);
|
||||
|
||||
company.Name.Should().Be(Name);
|
||||
CheckFields(company, M2Price, HourlyWage, Margin, workers)
|
||||
.Should().BeTrue("ctor has to set all fields correctly");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PatternStyle.Simple, 1, 75D)]
|
||||
[InlineData(PatternStyle.Simple, 2, 55D)]
|
||||
[InlineData(PatternStyle.Simple, 3, 45D)]
|
||||
[InlineData(PatternStyle.Simple, 4, 20D)]
|
||||
[InlineData(PatternStyle.Complex, 1, 37.5D)]
|
||||
[InlineData(PatternStyle.Complex, 2, 27.5D)]
|
||||
[InlineData(PatternStyle.Complex, 3, 22.5D)]
|
||||
[InlineData(PatternStyle.Complex, 4, 10D)]
|
||||
public void CalcPiecesPerHour(PatternStyle style, int workerSet, double expectedPpH)
|
||||
{
|
||||
var company = new Company("B", 0, 0,
|
||||
0, CreateSampleWorkers(workerSet));
|
||||
|
||||
CallCalcPiecesPerHour(company, style)
|
||||
.Should().Be(expectedPpH, "for this set of workers and pattern style");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetCostEstimateData))]
|
||||
public void GetCostEstimate(decimal m2Price, decimal hourlyWage, int profitMarginPercent,
|
||||
Worker[] workers, TilePattern pattern, decimal expectedCost, string testCase)
|
||||
{
|
||||
var company = new Company("C", m2Price, hourlyWage, profitMarginPercent, workers);
|
||||
|
||||
company.GetCostEstimate(pattern)
|
||||
.Should().Be(expectedCost, testCase);
|
||||
}
|
||||
|
||||
public static TheoryData<decimal, decimal, int, Worker[], TilePattern, decimal, string> GetCostEstimateData()
|
||||
{
|
||||
return new TheoryData<decimal, decimal, int, Worker[], TilePattern, decimal, string>
|
||||
{
|
||||
{
|
||||
12.34M, 56.78M, 5, CreateSampleWorkers(1),
|
||||
CreatePattern(PatternStyle.Simple, 1), 20M, "simple case 1"
|
||||
},
|
||||
{
|
||||
12.34M, 87.65M, 5, CreateSampleWorkers(1),
|
||||
CreatePattern(PatternStyle.Simple, 1), 25M, "simple case 2"
|
||||
},
|
||||
{
|
||||
12.34M, 87.65M, 5, CreateSampleWorkers(1),
|
||||
CreatePattern(PatternStyle.Complex, 1), 39M, "simple case 3"
|
||||
},
|
||||
{
|
||||
12.34M, 87.65M, 5, CreateSampleWorkers(1),
|
||||
CreatePattern(PatternStyle.Simple, 2), 2351M, "bigger pattern, workers 1"
|
||||
},
|
||||
{
|
||||
12.34M, 87.65M, 9, CreateSampleWorkers(1),
|
||||
CreatePattern(PatternStyle.Simple, 2), 2368M, "bigger pattern, workers 1, higher profit margin"
|
||||
},
|
||||
{
|
||||
12.34M, 87.65M, 5, CreateSampleWorkers(2),
|
||||
CreatePattern(PatternStyle.Simple, 2), 2182M, "bigger pattern, workers 2"
|
||||
},
|
||||
{
|
||||
60M, 87.65M, 5, CreateSampleWorkers(2),
|
||||
CreatePattern(PatternStyle.Simple, 2), 2280M, "bigger pattern, workers 2, higher m2 price"
|
||||
},
|
||||
{
|
||||
12.34M, 87.65M, 5, CreateSampleWorkers(3),
|
||||
CreatePattern(PatternStyle.Simple, 2), 2557M, "bigger pattern, workers 3"
|
||||
},
|
||||
{
|
||||
12.34M, 35.45M, 5, CreateSampleWorkers(3),
|
||||
CreatePattern(PatternStyle.Simple, 2), 1328M, "bigger pattern, workers 3, lower hourly price"
|
||||
},
|
||||
{
|
||||
12.34M, 87.65M, 5, CreateSampleWorkers(4),
|
||||
CreatePattern(PatternStyle.Simple, 2), 2815M, "bigger pattern, workers 4"
|
||||
},
|
||||
{
|
||||
12.34M, 87.65M, 5, CreateSampleWorkers(4),
|
||||
CreatePattern(PatternStyle.Complex, 2), 5138M, "bigger pattern, workers 4, complex pattern"
|
||||
}
|
||||
};
|
||||
|
||||
static TilePattern CreatePattern(PatternStyle style, int tileSet) => new(style, CreateSampleTiles(tileSet));
|
||||
}
|
||||
|
||||
private static Worker[] CreateSampleWorkers(int set)
|
||||
{
|
||||
return set switch
|
||||
{
|
||||
1 =>
|
||||
[
|
||||
new Worker("Franz Felser", WorkSpeed.Regular),
|
||||
new Worker("Marlene Marmor", WorkSpeed.Fast),
|
||||
new Worker("Ludwig Langsam", WorkSpeed.Slow)
|
||||
],
|
||||
2 =>
|
||||
[
|
||||
new Worker("Franz Felser", WorkSpeed.Fast),
|
||||
new Worker("Marlene Marmor", WorkSpeed.Regular)
|
||||
],
|
||||
3 =>
|
||||
[
|
||||
new Worker("Franz Felser", WorkSpeed.Slow),
|
||||
new Worker("Marlene Marmor", WorkSpeed.Regular)
|
||||
],
|
||||
4 =>
|
||||
[
|
||||
new Worker("Franz Felser", WorkSpeed.Slow)
|
||||
],
|
||||
_ => []
|
||||
};
|
||||
}
|
||||
|
||||
#region test helper methods - ignore
|
||||
|
||||
private static double CallCalcPiecesPerHour(Company instance, PatternStyle patternStyle)
|
||||
{
|
||||
var method = typeof(Company).GetMethod("CalcPiecesPerHour",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (method == null)
|
||||
{
|
||||
return -1D;
|
||||
}
|
||||
|
||||
return (double) method.Invoke(instance, [patternStyle])!;
|
||||
}
|
||||
|
||||
private static bool CheckFields(Company instance, decimal m2Price, decimal hourlyWage,
|
||||
int margin, Worker[] workers) =>
|
||||
CheckField(instance, m2Price, Prefix(nameof(m2Price)))
|
||||
&& CheckField(instance, hourlyWage, Prefix(nameof(hourlyWage)))
|
||||
&& CheckField(instance, margin, Prefix("profitMargin"))
|
||||
&& CheckField(instance, workers, Prefix(nameof(workers)));
|
||||
|
||||
#endregion
|
||||
}
|
||||
29
Mosaic.Test/Mosaic.Test.csproj
Normal file
29
Mosaic.Test/Mosaic.Test.csproj
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Mosaic\Mosaic.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
59
Mosaic.Test/TestBase.cs
Normal file
59
Mosaic.Test/TestBase.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#region test helper class - ignore
|
||||
|
||||
global using FluentAssertions;
|
||||
global using Xunit;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Mosaic.Test;
|
||||
|
||||
public abstract class TestBase
|
||||
{
|
||||
protected static bool CheckField<TClass, TField>(TClass instance, TField expectedValue, string expectedName)
|
||||
{
|
||||
var field = typeof(TClass)
|
||||
.GetField(expectedName, BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (field == null
|
||||
|| field.FieldType != typeof(TField))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
object? value = field.GetValue(instance);
|
||||
return value != null && value.Equals(expectedValue);
|
||||
}
|
||||
|
||||
protected static string Prefix(string n) => $"_{n}";
|
||||
|
||||
protected static Tile[] CreateSampleTiles(int set = 1)
|
||||
{
|
||||
return set switch
|
||||
{
|
||||
1 =>
|
||||
[
|
||||
new Tile(TileStyle.Polished, 10, 10),
|
||||
new Tile(TileStyle.FancyColor, 15, 30),
|
||||
new Tile(TileStyle.Raw, 200, 200),
|
||||
new Tile(TileStyle.Ornate, 20, 20)
|
||||
],
|
||||
2 => CreateTiles(20, 20, TileStyle.FancyColor, 100)
|
||||
.Concat(CreateTiles(35, 35, TileStyle.Polished, 350))
|
||||
.Concat(CreateTiles(200, 100, TileStyle.Raw, 80))
|
||||
.ToArray(),
|
||||
_ => []
|
||||
};
|
||||
|
||||
static Tile[] CreateTiles(int width, int height, TileStyle style, int amount)
|
||||
{
|
||||
var tiles = new Tile[amount];
|
||||
var idx = 0;
|
||||
for (var i = 0; i < amount; i++)
|
||||
{
|
||||
tiles[idx++] = new Tile(style, width, height);
|
||||
}
|
||||
|
||||
return tiles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
50
Mosaic.Test/TilePatternTests.cs
Normal file
50
Mosaic.Test/TilePatternTests.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
namespace Mosaic.Test;
|
||||
|
||||
public sealed class TilePatternTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public void Area()
|
||||
{
|
||||
Tile[] tiles = CreateSampleTiles();
|
||||
var pattern = new TilePattern(PatternStyle.Simple, tiles);
|
||||
|
||||
pattern.Area
|
||||
.Should().Be(0.04095, "the pattern reports its area in m2");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalcProductionCost()
|
||||
{
|
||||
Tile[] tiles = CreateSampleTiles();
|
||||
var pattern1 = new TilePattern(PatternStyle.Simple, tiles);
|
||||
var pattern2 = new TilePattern(PatternStyle.Complex, tiles);
|
||||
|
||||
pattern1.CalcProductionCost()
|
||||
.Should().Be(9.4616M, "the production cost of a pattern is the sum of the production cost of its tiles");
|
||||
pattern2.CalcProductionCost()
|
||||
.Should().Be(pattern1.CalcProductionCost(), "pattern style does not change the production cost");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Construction()
|
||||
{
|
||||
Tile[] tiles = CreateSampleTiles();
|
||||
|
||||
var instance = new TilePattern(PatternStyle.Complex, tiles);
|
||||
|
||||
instance.Style
|
||||
.Should().Be(PatternStyle.Complex, "ctor has to set Style property correctly");
|
||||
CheckField(instance, tiles, $"_{nameof(tiles)}")
|
||||
.Should().BeTrue("ctor has to set tiles field correctly");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pieces()
|
||||
{
|
||||
Tile[] tiles = CreateSampleTiles();
|
||||
var pattern = new TilePattern(PatternStyle.Simple, tiles);
|
||||
|
||||
pattern.Pieces
|
||||
.Should().Be(tiles.Length, "a pattern has as many pieces as it has individual tiles");
|
||||
}
|
||||
}
|
||||
87
Mosaic.Test/TileTests.cs
Normal file
87
Mosaic.Test/TileTests.cs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
namespace Mosaic.Test;
|
||||
|
||||
public sealed class TileTests : TestBase
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(12, 12, 144)]
|
||||
[InlineData(20, 16, 320)]
|
||||
[InlineData(30, 30, 900)]
|
||||
[InlineData(8, 8, 64)]
|
||||
[InlineData(10, 20, 200)]
|
||||
public void Area(int width, int height, int expectedArea)
|
||||
{
|
||||
var tile = new Tile(TileStyle.Raw, width, height);
|
||||
|
||||
tile.Area
|
||||
.Should().Be(expectedArea);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(CalcProductionCostData))]
|
||||
public void CalcProductionCost(TileStyle style, int width, int height, decimal expectedPrice, string reason)
|
||||
{
|
||||
var tile = new Tile(style, width, height);
|
||||
|
||||
tile.CalcProductionCost()
|
||||
.Should().Be(expectedPrice, reason);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(TileStyle.PlainColor, 12, 50)]
|
||||
[InlineData(TileStyle.Raw, 43, 8)]
|
||||
[InlineData(TileStyle.SimplePattern, 20, 20)]
|
||||
[InlineData(TileStyle.Ornate, 500, 500)]
|
||||
public void Construction(TileStyle style, int width, int height)
|
||||
{
|
||||
var instance = new Tile(style, width, height);
|
||||
|
||||
CheckFields(instance, style, width, height)
|
||||
.Should().BeTrue("ctor has to set value of all three fields correctly");
|
||||
}
|
||||
|
||||
public static TheoryData<TileStyle, int, int, decimal, string> CalcProductionCostData()
|
||||
{
|
||||
const int BaseWidth = 20;
|
||||
const int BaseHeight = BaseWidth;
|
||||
const TileStyle BaseStyle = TileStyle.Polished;
|
||||
|
||||
return new TheoryData<TileStyle, int, int, decimal, string>
|
||||
{
|
||||
{ BaseStyle, BaseWidth, BaseHeight, 0.064M, "no modifiers applied to base cm2 price" },
|
||||
{ TileStyle.Raw, BaseWidth, BaseHeight, 0.0512M, "raw tiles are cheaper" },
|
||||
{ TileStyle.PlainColor, BaseWidth, BaseHeight, 0.064M, "no modifiers applied" },
|
||||
|
||||
{
|
||||
TileStyle.FancyColor, BaseWidth, BaseHeight, 0.0704M, "fancy colored tiles are slightly more expensive"
|
||||
},
|
||||
|
||||
{
|
||||
TileStyle.SimplePattern, BaseWidth, BaseHeight, 0.08M, "tiles with a pattern are even more expensive"
|
||||
},
|
||||
|
||||
{
|
||||
TileStyle.Ornate, BaseWidth, BaseHeight, 0.1472M,
|
||||
"hand colored tiles with an ornate pattern are very expensive"
|
||||
},
|
||||
{ BaseStyle, 8, 8, 0.01536M, "very small tiles are more expensive" },
|
||||
{ BaseStyle, 15, 15, 0.0432M, "small tiles are a little more expensive" },
|
||||
{ BaseStyle, 30, 30, 0.144M, "regular sized tile" },
|
||||
{ BaseStyle, 45, 20, 0.144M, "price is based on area not width/height relation" },
|
||||
{ BaseStyle, 20, 45, 0.144M, "price is based on area not width/height relation" },
|
||||
{ BaseStyle, 50, 55, 0.704M, "bigger tiles are a little more expensive" },
|
||||
{ BaseStyle, 50, 50, 0.4M, "this size is still normally priced" },
|
||||
{ BaseStyle, 90, 95, 2.4624M, "big tiles are more expensive" },
|
||||
|
||||
{ TileStyle.SimplePattern, 200, 20, 1.28M, "a combination of size and style modifiers is applied" }
|
||||
};
|
||||
}
|
||||
|
||||
#region test helper - ignore
|
||||
|
||||
private static bool CheckFields(Tile tile, TileStyle style, int width, int height) =>
|
||||
CheckField(tile, style, Prefix(nameof(style)))
|
||||
&& CheckField(tile, width, Prefix(nameof(width)))
|
||||
&& CheckField(tile, height, Prefix(nameof(height)));
|
||||
|
||||
#endregion
|
||||
}
|
||||
31
Mosaic.sln
Normal file
31
Mosaic.sln
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.3.32922.545
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mosaic", "Mosaic\Mosaic.csproj", "{BAAAD012-4047-4220-A476-E9C432F3FC1E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mosaic.Test", "Mosaic.Test\Mosaic.Test.csproj", "{2BD750D6-37D8-4F3B-AD51-AF35D8AB4590}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{BAAAD012-4047-4220-A476-E9C432F3FC1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BAAAD012-4047-4220-A476-E9C432F3FC1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BAAAD012-4047-4220-A476-E9C432F3FC1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BAAAD012-4047-4220-A476-E9C432F3FC1E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2BD750D6-37D8-4F3B-AD51-AF35D8AB4590}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2BD750D6-37D8-4F3B-AD51-AF35D8AB4590}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2BD750D6-37D8-4F3B-AD51-AF35D8AB4590}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2BD750D6-37D8-4F3B-AD51-AF35D8AB4590}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {4BC72C42-3482-4430-85F7-6B56B062292D}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
55
Mosaic/Company.cs
Normal file
55
Mosaic/Company.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
namespace Mosaic;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a flooring company
|
||||
/// </summary>
|
||||
public sealed class Company
|
||||
{
|
||||
private const int DefaultPiecesPerHour = 25;
|
||||
private readonly decimal _hourlyWage;
|
||||
private readonly decimal _m2Price;
|
||||
private readonly int _profitMargin;
|
||||
private readonly Worker[] _workers;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Company"/> instance based on the supplied configuration.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the company</param>
|
||||
/// <param name="m2Price">Base price per m2 of floor, independent of pattern type</param>
|
||||
/// <param name="hourlyWage">Hourly wage of each worker (paid by customer)</param>
|
||||
/// <param name="profitMarginPercent">Profit margin put on top of the production cost of the tiles</param>
|
||||
/// <param name="workers">Array of the company's employees</param>
|
||||
public Company(string name, decimal m2Price, decimal hourlyWage,
|
||||
int profitMarginPercent, Worker[] workers)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the company
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Calculates how much this specific company would charge when tasked with executing the
|
||||
/// supplied pattern. This includes production cost as well as work costs.
|
||||
/// </summary>
|
||||
/// <param name="pattern">Pattern to create</param>
|
||||
/// <returns>Cost estimate for the supplied pattern</returns>
|
||||
public decimal GetCostEstimate(TilePattern pattern)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates how many pieces the workers of this company (together) are able to place per hour.
|
||||
/// This takes into account the complexity of the pattern as well as the working speed of
|
||||
/// each employee.
|
||||
/// </summary>
|
||||
/// <param name="patternStyle">Defines if the pattern is simple or complex</param>
|
||||
/// <returns>Number of tiles this company is able to place per hour</returns>
|
||||
private double CalcPiecesPerHour(PatternStyle patternStyle)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
26
Mosaic/Model.cs
Normal file
26
Mosaic/Model.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
namespace Mosaic;
|
||||
|
||||
public enum TileStyle
|
||||
{
|
||||
Raw,
|
||||
Polished,
|
||||
PlainColor,
|
||||
FancyColor,
|
||||
SimplePattern,
|
||||
Ornate
|
||||
}
|
||||
|
||||
public enum PatternStyle
|
||||
{
|
||||
Simple,
|
||||
Complex
|
||||
}
|
||||
|
||||
public enum WorkSpeed
|
||||
{
|
||||
Slow,
|
||||
Regular,
|
||||
Fast
|
||||
}
|
||||
|
||||
public record Worker(string Name, WorkSpeed WorkSpeed);
|
||||
11
Mosaic/Mosaic.csproj
Normal file
11
Mosaic/Mosaic.csproj
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
82
Mosaic/Program.cs
Normal file
82
Mosaic/Program.cs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
using System.Text;
|
||||
using Mosaic;
|
||||
|
||||
Console.OutputEncoding = Encoding.UTF8;
|
||||
|
||||
Console.WriteLine($"*** Mosaic Prices ***{Environment.NewLine}");
|
||||
|
||||
var patterns = new TilePattern[]
|
||||
{
|
||||
new(PatternStyle.Simple, CreateTiles([
|
||||
new TileConfig(15, 15, TileStyle.PlainColor, 200),
|
||||
new TileConfig(80, 40, TileStyle.SimplePattern, 160),
|
||||
new TileConfig(20, 20, TileStyle.Polished, 400),
|
||||
new TileConfig(200, 100, TileStyle.Raw, 120)
|
||||
])),
|
||||
new(PatternStyle.Complex, CreateTiles([
|
||||
new TileConfig(12, 12, TileStyle.FancyColor, 318),
|
||||
new TileConfig(20, 10, TileStyle.Ornate, 102),
|
||||
new TileConfig(30, 30, TileStyle.SimplePattern, 200)
|
||||
]))
|
||||
};
|
||||
Company[] companies =
|
||||
[
|
||||
new("Fein & Stein GmbH", 50, 80, 5, new Worker[]
|
||||
{
|
||||
new("Hans Heimlich", WorkSpeed.Slow),
|
||||
new("Sarah Schnell", WorkSpeed.Fast)
|
||||
}),
|
||||
new("Flooring Perfection AG", 92, 119, 9, new Worker[]
|
||||
{
|
||||
new("Petra Perfekt", WorkSpeed.Slow),
|
||||
new("Moritz Meister", WorkSpeed.Regular),
|
||||
new("Günther Genau", WorkSpeed.Slow)
|
||||
}),
|
||||
new("Schnellbelag GmbH", 38, 56, 4, new Worker[]
|
||||
{
|
||||
new("Paul Pfusch", WorkSpeed.Fast)
|
||||
})
|
||||
];
|
||||
|
||||
for (var i = 0; i < patterns.Length; i++)
|
||||
{
|
||||
PrintEstimates(i + 1, patterns[i], companies);
|
||||
}
|
||||
|
||||
#region helper methods
|
||||
|
||||
static void PrintEstimates(int no, TilePattern pattern, Company[] companies)
|
||||
{
|
||||
Console.WriteLine($"For pattern #{no} the companies provided the following quotes:");
|
||||
foreach (var company in companies)
|
||||
{
|
||||
Console.WriteLine($"{company.Name}: {company.GetCostEstimate(pattern):C2}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
static Tile[] CreateTiles(TileConfig[] configs)
|
||||
{
|
||||
var totalTiles = 0;
|
||||
foreach (var config in configs)
|
||||
{
|
||||
totalTiles += config.Amount;
|
||||
}
|
||||
|
||||
var tiles = new Tile[totalTiles];
|
||||
var idx = 0;
|
||||
foreach (var config in configs)
|
||||
{
|
||||
for (var i = 0; i < config.Amount; i++)
|
||||
{
|
||||
tiles[idx++] = new Tile(config.Style, config.Width, config.Height);
|
||||
}
|
||||
}
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
internal record TileConfig(int Width, int Height, TileStyle Style, int Amount);
|
||||
|
||||
#endregion
|
||||
9
Mosaic/Tile.cs
Normal file
9
Mosaic/Tile.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
namespace Mosaic;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single tile of a mosaic floor.
|
||||
/// </summary>
|
||||
public sealed class Tile
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
9
Mosaic/TilePattern.cs
Normal file
9
Mosaic/TilePattern.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
namespace Mosaic;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mosaic consisting of several tiles.
|
||||
/// </summary>
|
||||
public class TilePattern
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
BIN
pics/mosaic.png
Normal file
BIN
pics/mosaic.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 508 KiB |
BIN
pics/pattern_sample.jpg
Normal file
BIN
pics/pattern_sample.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
BIN
pics/sample_run.png
Normal file
BIN
pics/sample_run.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
pics/tile_examples.png
Normal file
BIN
pics/tile_examples.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
184
readme.adoc
Normal file
184
readme.adoc
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
:sectnums:
|
||||
:nofooter:
|
||||
:toc: left
|
||||
:icons: font
|
||||
:data-uri:
|
||||
:source-highlighter: highlightjs
|
||||
:stem: latexmath
|
||||
|
||||
= OOB.04 -- Mosaic
|
||||
|
||||
In this exercise you will represent mosaic floor patterns as objects and have various companies calculate an offer for constructing them.
|
||||
|
||||
image::pics/mosaic.png[Roman Mosaic Floor]
|
||||
|
||||
[plantuml]
|
||||
----
|
||||
@startuml
|
||||
|
||||
hide empty methods
|
||||
|
||||
enum WorkSpeed {
|
||||
Slow
|
||||
Regular
|
||||
Fast
|
||||
}
|
||||
|
||||
enum PatternStyle {
|
||||
Simple
|
||||
Complex
|
||||
}
|
||||
|
||||
enum TileStyle {
|
||||
Raw
|
||||
Polished
|
||||
PlainColor
|
||||
FancyColor
|
||||
SimplePattern
|
||||
Ornate
|
||||
}
|
||||
|
||||
class Worker {
|
||||
+string Name [readonly]
|
||||
+WorkSpeed WorkSpeed [readonly]
|
||||
}
|
||||
|
||||
class Company {
|
||||
-int DefaultPiecesPerHour [const]
|
||||
-decimal _hourlyWage [readonly]
|
||||
-decimal _m2Price [readonly]
|
||||
-int _profitMargin [readonly]
|
||||
-Worker[] _workers [readonly]
|
||||
+string Name [readonly]
|
||||
|
||||
+Company(string, decimal, decimal, int, Worker[])
|
||||
+decimal GetCostEstimate(TilePattern)
|
||||
-double CalcPiecesPerHour(PatternStyle)
|
||||
}
|
||||
|
||||
class Tile {
|
||||
-TileStyle _style [readonly]
|
||||
-int _width [readonly]
|
||||
-int _height [readonly]
|
||||
+int Area [readonly]
|
||||
|
||||
+Tile(TileStyle, int, int)
|
||||
+decimal CalcProductionCost()
|
||||
}
|
||||
|
||||
class TilePattern {
|
||||
-Tile[] _tiles [readonly]
|
||||
+int Pieces [readonly]
|
||||
+double Area [readonly]
|
||||
+PatternStyle Style [readonly]
|
||||
|
||||
+TilePattern(PatternStyle, Tile[])
|
||||
+decimal CalcProductionCost()
|
||||
}
|
||||
|
||||
Company "1" -- "n" Worker: employs
|
||||
Worker -r- WorkSpeed
|
||||
TilePattern "1" -- "n" Tile: consists of
|
||||
Tile -r- TileStyle
|
||||
TilePattern -r- PatternStyle
|
||||
|
||||
@enduml
|
||||
----
|
||||
|
||||
WARNING: Some implementation as well as unit tests have been provided to you. Consider commenting some code to allow the application to compile until you have implemented all missing pieces. Also make sure to use the correct names and method signatures or the unit tests (and `Program`) won't work.
|
||||
|
||||
== `Tile` class
|
||||
|
||||
A single mosaic tile in a pattern.
|
||||
It is defined by a specific style and a size (in mm).
|
||||
Based on this information it can calculate its own production cost.
|
||||
|
||||
Tiles can come in various styles with increasing production difficulty.
|
||||
For example a tile could have only a polished finish or a multicolored ornate pattern.
|
||||
|
||||
image::pics/tile_examples.png[Tile Examples]
|
||||
|
||||
=== Production cost calculation
|
||||
|
||||
The following rules apply when calculating the production cost for a single tile.
|
||||
|
||||
* Each tile has a base price of 0.016€ per stem:[cm^2]
|
||||
* Size modification factors -- very small or very large tiles are more expensive than a standard size
|
||||
** stem:[a < 100 mm^2] => x1.5
|
||||
** stem:[a < 400 mm^2] => x1.2
|
||||
** stem:[400 \le a \le 2500] => x1
|
||||
** stem:[a > 2500 mm^2] => x1.6
|
||||
** stem:[a > 8100 mm^2] => x1.8
|
||||
** Only _one_ of those modifiers is applied
|
||||
*** So for size stem:[50mm^2] it would be x1.5 and _not_ x2.7
|
||||
* Fancier tiles are more expensive to produce, so the production cost is increased by the following factors:
|
||||
** `Raw` => x0.8
|
||||
** `Polished` & `PlainColor` => x1
|
||||
** `FancyColor` => x1.1
|
||||
** `SimplePattern` => x1.25
|
||||
** `Ornate` => x2.3
|
||||
|
||||
Final calculation: stem:[areaInCm^2 * pricePerCm^2 * sizeFactor * styleFactor]
|
||||
|
||||
== `TilePattern` class
|
||||
|
||||
This class represents a combination of individual tiles in various shapes and colors to form a pattern.
|
||||
Cost estimates are always created by companies for a whole pattern as a single unit.
|
||||
|
||||
image::pics/pattern_sample.jpg[Pattern example, width=400]
|
||||
|
||||
=== Implementation Hints
|
||||
|
||||
* The number of pieces equals the number of tiles contained in the pattern
|
||||
* The area is the sum of the area of all tiles in stem:[m^2]
|
||||
** We are ignoring the space the joints between the tiles would take
|
||||
* The production cost is simply the sum of the production cost of all tiles
|
||||
** Pattern style does not change the production cost
|
||||
*** It will make a difference when calculating the work cost later
|
||||
|
||||
TIP: It is possible to define numbers in scientific notation, e.g. `1E-6D`
|
||||
|
||||
== `Company` class
|
||||
|
||||
Each instance of this class represents one company which specializes in laying mosaic floors.
|
||||
|
||||
* A company employees one or more `Worker`
|
||||
* It demands a certain base price per stem:[m^2] laid
|
||||
* For _each_ of the workers a certain hourly wage has to be paid
|
||||
* All companies pay the same production cost for the tiles, but they put different amounts of a profit margin on top of that
|
||||
** e.g. 100€ production cost, 5% margin => 105€ total price for the material
|
||||
** We are ignoring costs for joints material etc.
|
||||
|
||||
NOTE: `Worker` is a simple, already provided `record` without any operations -- also the `Name` property is just flavor. You don't have to implement any functionality for it.
|
||||
|
||||
=== Pieces per hour
|
||||
|
||||
Each worker can work either slowly, normal or fast:
|
||||
|
||||
* With regular speed 25 tiles can be laid down per hour.
|
||||
* A fast worker is 20% faster
|
||||
* A slow worker is 20% slower
|
||||
|
||||
It takes _twice_ as long to work on a `Complex` pattern than on an easy one -- the default speed assumes a `Simple` pattern.
|
||||
|
||||
On each job all workers are working together and at the same time.
|
||||
So the total amount of pieces laid per hour is based on the stats of all workers of the company, adjusted by their individual speed and modified if the pattern is complex.
|
||||
|
||||
=== Cost calculation
|
||||
|
||||
The total quote a company calculates for laying a mosaic is based on the following parts:
|
||||
|
||||
. The base price which has to be paid for the total amount of stem:[m^2] the mosaic covers
|
||||
. The work time
|
||||
** Time needed to construct the pattern, multiplied by hourly wage
|
||||
. The production cost of the tiles
|
||||
** Plus profit margin on top
|
||||
|
||||
The final amount is rounded _up_ to the next whole number.
|
||||
|
||||
== `Program` & Sample Run
|
||||
|
||||
The `Program` class is already complete.
|
||||
It will create some sample tile patterns and companies from which it requests cost estimates for those.
|
||||
|
||||
image::pics/sample_run.png[Sample Run]
|
||||
Loading…
Add table
Add a link
Reference in a new issue