diff --git a/app/controllers/bootcamp/projects_controller.rb b/app/controllers/bootcamp/projects_controller.rb index 18dcb3c6ef..746a41a8b0 100644 --- a/app/controllers/bootcamp/projects_controller.rb +++ b/app/controllers/bootcamp/projects_controller.rb @@ -2,7 +2,7 @@ class Bootcamp::ProjectsController < Bootcamp::BaseController before_action :use_project, only: %i[show] def index - @user_projects = current_user.bootcamp_user_projects + @user_projects = current_user.bootcamp_user_projects.index_by(&:project_id) end def show diff --git a/app/css/bootcamp/components/codemirror.css b/app/css/bootcamp/components/codemirror.css index e0cc3d8f55..67ace6c00f 100644 --- a/app/css/bootcamp/components/codemirror.css +++ b/app/css/bootcamp/components/codemirror.css @@ -13,7 +13,7 @@ @apply flex flex-grow; .cm-gutters { - background: transparent; + background: white; @apply border-r-0; } .cm-lineNumbers .cm-gutterElement, diff --git a/app/css/bootcamp/components/exercise-widget.css b/app/css/bootcamp/components/exercise-widget.css index 8cce57af64..edb805e0de 100644 --- a/app/css/bootcamp/components/exercise-widget.css +++ b/app/css/bootcamp/components/exercise-widget.css @@ -1,5 +1,5 @@ .c-exercise-widget { - @apply py-12 px-12 rounded-8 border-1 block; + @apply py-12 px-12 rounded-8 border-1 border-borderColor5 block; @apply flex flex-col items-stretch; @apply bg-white; diff --git a/app/css/bootcamp/components/project-widget.css b/app/css/bootcamp/components/project-widget.css index 33ec217f9d..f607675d73 100644 --- a/app/css/bootcamp/components/project-widget.css +++ b/app/css/bootcamp/components/project-widget.css @@ -1,5 +1,5 @@ .c-project-widget { - @apply py-12 px-16 rounded-8 border-1 block; + @apply py-12 px-16 rounded-8 border-1 border-borderColor5 block; @apply flex flex-col items-stretch; @apply bg-white; @apply relative; diff --git a/app/css/bootcamp/pages/dashboard.css b/app/css/bootcamp/pages/dashboard.css index f8b784835e..562261ce8c 100644 --- a/app/css/bootcamp/pages/dashboard.css +++ b/app/css/bootcamp/pages/dashboard.css @@ -3,35 +3,45 @@ body.namespace-bootcamp.controller-dashboard.action-index { } #page-bootcamp-dashboard { - @apply pt-24; - h1 { - @apply text-[36px] leading-140; - @apply font-bold text-textColor1; - @apply mb-4; - } - h2 { - @apply text-22 leading-150; - @apply font-semibold text-textColor1; - @apply mt-20 mb-4; - } - p.large { - @apply text-20 leading-150; - @apply mb-8; - } - p, - ul { - @apply text-18 leading-150; - @apply mb-8; - } - ul { - @apply list-disc pl-20; + section.intro { + @apply pt-24; + h1 { + @apply text-[36px] leading-140; + @apply font-bold text-textColor1; + @apply mb-4; + } + h2 { + @apply text-22 leading-150; + @apply font-semibold text-textColor1; + @apply mt-20 mb-4; + } + p.large { + @apply text-20 leading-150; + @apply mb-8; + } + p, + ul { + @apply text-16 leading-150; + @apply mb-8; + } + ul { + @apply list-disc pl-20; + } } - .exercise { - @apply py-12 px-16 rounded-8 border-1 block; - @apply shadow-base flex flex-col items-stretch; - @apply bg-white; - + section.normal { + @apply pt-20; + .exercise { + @paply w-full; + @apply py-12 px-16 rounded-8 border-1 border-borderColor5 block; + @apply shadow-base flex flex-col items-stretch; + @apply bg-white; + } + h2 { + @apply text-22 leading-150; + @apply font-semibold text-textColor1; + @apply mb-4; + } h3 { @apply text-20 leading-150; @apply mb-4; @@ -56,7 +66,7 @@ body.namespace-bootcamp.controller-dashboard.action-index { } } p { - @apply text-15 leading-140; + @apply text-16 leading-140; } } } diff --git a/app/helpers/view_components/bootcamp/exercise_widget.rb b/app/helpers/view_components/bootcamp/exercise_widget.rb index 25b9e85012..d7fd7aea6d 100644 --- a/app/helpers/view_components/bootcamp/exercise_widget.rb +++ b/app/helpers/view_components/bootcamp/exercise_widget.rb @@ -23,7 +23,7 @@ def solution memoize def user_project - @user_project || UserProject.for!(current_user, exercise.project) + @user_project || ::Bootcamp::UserProject.for!(current_user, exercise.project) end end end diff --git a/app/javascript/components/bootcamp/SolveExercisePage/Instructions/Instructions.tsx b/app/javascript/components/bootcamp/SolveExercisePage/Instructions/Instructions.tsx index 0a8c9396ab..de7058a4e7 100644 --- a/app/javascript/components/bootcamp/SolveExercisePage/Instructions/Instructions.tsx +++ b/app/javascript/components/bootcamp/SolveExercisePage/Instructions/Instructions.tsx @@ -68,7 +68,8 @@ export function _Instructions({ Task {activeTaskIndex + 1}: {currentTask?.name} {/* "inline" is required to keep the cursor on the same line */} -
+ {/**/} + > )} diff --git a/app/javascript/components/bootcamp/SolveExercisePage/exercises/draw/DrawExercise.tsx b/app/javascript/components/bootcamp/SolveExercisePage/exercises/draw/DrawExercise.tsx index 84986d6f13..45b34c8996 100644 --- a/app/javascript/components/bootcamp/SolveExercisePage/exercises/draw/DrawExercise.tsx +++ b/app/javascript/components/bootcamp/SolveExercisePage/exercises/draw/DrawExercise.tsx @@ -57,12 +57,16 @@ class Triangle extends Shape { } } +export type FillColor = + | { type: 'hex'; color: string } + | { type: 'rgb'; color: [number, number, number] } + export default class DrawExercise extends Exercise { private canvas: HTMLDivElement private shapes: Shape[] = [] private penColor = '#333333' - private fillColor = '#ff0000' + private fillColor: FillColor = { type: 'hex', color: '#ff0000' } constructor() { super('draw') @@ -89,12 +93,20 @@ export default class DrawExercise extends Exercise { public getRectangleAt(x: number, y: number, width: number, height: number) { return this.shapes.find((shape) => { if (shape instanceof Rectangle) { - return ( - shape.x == x && - shape.y == y && - shape.width == width && - shape.height == height - ) + if (shape.x != x || shape.y != y) { + return false + } + if (width !== undefined) { + if (shape.width != width) { + return false + } + } + if (height !== undefined) { + if (shape.height != height) { + return false + } + } + return true } }) } @@ -105,6 +117,13 @@ export default class DrawExercise extends Exercise { } }) } + public getEllipseAt(x: number, y: number, rx: number, ry: number) { + return this.shapes.find((shape) => { + if (shape instanceof Ellipse) { + return shape.x == x && shape.y == y && shape.rx == rx && shape.ry == ry + } + }) + } public getTriangleAt( x1: number, y1: number, @@ -130,8 +149,11 @@ export default class DrawExercise extends Exercise { public changePenColor(executionCtx: ExecutionContext, color: string) { this.penColor = color } - public changeFillColor(executionCtx: ExecutionContext, color: string) { - this.fillColor = color + public fillColorHex(executionCtx: ExecutionContext, color: string) { + this.fillColor = { type: 'hex', color: color } + } + public fillColorRGB(executionCtx: ExecutionContext, red, green, blue) { + this.fillColor = { type: 'rgb', color: [red, green, blue] } } public rectangle( @@ -327,6 +349,12 @@ export default class DrawExercise extends Exercise { this.shapes = [] } + public setBackgroundImage(imageUrl: string) { + this.canvas.style.backgroundImage = 'url(' + imageUrl + ')' + this.canvas.style.backgroundSize = '99.5%' + this.canvas.style.backgroundPosition = 'center' + } + public availableFunctions = [ { name: 'rand', @@ -381,9 +409,14 @@ export default class DrawExercise extends Exercise { description: 'Changes the pen color', }, { - name: 'change_fill_color', - func: this.changeFillColor.bind(this), - description: 'Changes the fill color', + name: 'fill_color_hex', + func: this.fillColorHex.bind(this), + description: 'Changes the fill color using a hex string', + }, + { + name: 'fill_color_rgb', + func: this.fillColorRGB.bind(this), + description: 'Changes the fill color using three RGB values', }, ] } diff --git a/app/javascript/components/bootcamp/SolveExercisePage/exercises/draw/shapes.ts b/app/javascript/components/bootcamp/SolveExercisePage/exercises/draw/shapes.ts index d99eb1e156..87afd62890 100644 --- a/app/javascript/components/bootcamp/SolveExercisePage/exercises/draw/shapes.ts +++ b/app/javascript/components/bootcamp/SolveExercisePage/exercises/draw/shapes.ts @@ -1,3 +1,5 @@ +import { FillColor } from './DrawExercise' + const svgNS = 'http://www.w3.org/2000/svg' function createSVG(children) { @@ -18,12 +20,22 @@ function createSVG(children) { return svg } -function createSVGElement(type: string, backgroundColor, penColor, attrs) { +function createSVGElement( + type: string, + backgroundColor: FillColor, + penColor, + attrs +) { const elem = document.createElementNS(svgNS, type) - elem.setAttribute('fill', backgroundColor) elem.setAttribute('stroke', penColor) elem.setAttribute('stroke-width', '0') + if (backgroundColor.type === 'hex') { + elem.setAttribute('fill', backgroundColor.color) + } else { + elem.setAttribute('fill', 'rgb(' + backgroundColor.color.join(',') + ')') + } + for (const key in attrs) { elem.setAttribute(key, attrs[key]) } @@ -36,7 +48,7 @@ export function rect( width: number, height: number, penColor: string, - backgroundColor: string + backgroundColor: FillColor ) { const rect = createSVGElement('rect', backgroundColor, penColor, { x: x.toString(), @@ -53,7 +65,7 @@ export function circle( cy: number, radius: number, penColor: string, - backgroundColor: string + backgroundColor: FillColor ) { const circle = createSVGElement('circle', backgroundColor, penColor, { cx: cx.toString(), @@ -70,7 +82,7 @@ export function ellipse( rx: number, ry: number, penColor: string, - backgroundColor: string + backgroundColor: FillColor ) { const ellipse = createSVGElement('ellipse', backgroundColor, penColor, { cx: cx.toString(), @@ -90,7 +102,7 @@ export function triangle( x3: number, y3: number, penColor: string, - backgroundColor: string + backgroundColor: FillColor ) { const polygon = createSVGElement('polygon', backgroundColor, penColor, { points: `${x1},${y1} ${x2},${y2} ${x3},${y3}`, diff --git a/app/javascript/components/bootcamp/SolveExercisePage/test-runner/generateAndRunTestSuite/generateExpects.ts b/app/javascript/components/bootcamp/SolveExercisePage/test-runner/generateAndRunTestSuite/generateExpects.ts index b343bec362..0a8a5a0e61 100644 --- a/app/javascript/components/bootcamp/SolveExercisePage/test-runner/generateAndRunTestSuite/generateExpects.ts +++ b/app/javascript/components/bootcamp/SolveExercisePage/test-runner/generateAndRunTestSuite/generateExpects.ts @@ -57,7 +57,6 @@ function generateExpectsForStateTests(exercise: Exercise, testData: TaskTest) { // And then we get the function and call it. const fn = exercise[fnName] - console.log(fnName, fn, exercise) actual = fn.bind(exercise).call(exercise, ...args) } diff --git a/app/javascript/interpreter/error.ts b/app/javascript/interpreter/error.ts index d6542c03e8..6c53cf0f95 100644 --- a/app/javascript/interpreter/error.ts +++ b/app/javascript/interpreter/error.ts @@ -22,7 +22,8 @@ export type RuntimeErrorType = | 'RepeatCountMustBeGreaterThanZero' | 'NonCallableTarget' | 'InfiniteLoop' - | 'InvalidNumberOfArguments' + | 'TooFewArguments' + | 'TooManyArguments' | 'InvalidNumberOfArgumentsWithOptionalArguments' | 'InvalidUnaryOperator' | 'InvalidBinaryExpression' diff --git a/app/javascript/interpreter/executor.ts b/app/javascript/interpreter/executor.ts index 832e321f04..02c5c770c2 100644 --- a/app/javascript/interpreter/executor.ts +++ b/app/javascript/interpreter/executor.ts @@ -471,13 +471,10 @@ export class Executor const arity = callee.value.arity() const [minArity, maxArity] = isNumber(arity) ? [arity, arity] : arity + console.log(minArity, maxArity) + if (args.length < minArity || args.length > maxArity) { - if (minArity === maxArity) { - this.error('InvalidNumberOfArguments', expression.paren.location, { - arity: maxArity, - args, - }) - } else + if (minArity !== maxArity) { this.error( 'InvalidNumberOfArgumentsWithOptionalArguments', expression.paren.location, @@ -487,6 +484,21 @@ export class Executor numberOfArgs: args.length, } ) + } + + if (args.length < minArity) { + this.error('TooFewArguments', expression.paren.location, { + arity: maxArity, + numberOfArgs: args.length, + args, + }) + } else { + this.error('TooManyArguments', expression.paren.location, { + arity: maxArity, + numberOfArgs: args.length, + args, + }) + } } let value diff --git a/app/javascript/interpreter/locales/en/translation.json b/app/javascript/interpreter/locales/en/translation.json index 1895432fe2..738c76941e 100644 --- a/app/javascript/interpreter/locales/en/translation.json +++ b/app/javascript/interpreter/locales/en/translation.json @@ -74,7 +74,8 @@ "InvalidExpression": "Invalid expression", "InvalidIndexGetterTarget": "Can't index object, only dictionaries and arrays can be indexed'.", "InvalidIndexSetterTarget": "Can only set dictionaries and arrays via indexer.", - "InvalidNumberOfArguments": "Expected {{maxArity}} arguments but got {{numberOfArgs}}.", + "TooFewArguments": "Did you forget an argument? This function expects to be called with {{arity}} arguments but you called it with {{numberOfArgs}}.", + "TooManyArguments": "Did you add an extra argument? This function expects to be called with {{arity}} arguments but you called it with {{numberOfArgs}}.", "InvalidNumberOfArgumentsWithOptionalArguments": "Expected between {{minArity} and {{maxArity}} arguments but got {{numberOfArgs}}.", "InvalidUnaryOperator": "Invalid unary operator", "LogicError": "{{message}}", diff --git a/app/models/bootcamp/exercise.rb b/app/models/bootcamp/exercise.rb index 038837d990..3c19f7b416 100644 --- a/app/models/bootcamp/exercise.rb +++ b/app/models/bootcamp/exercise.rb @@ -11,7 +11,7 @@ class Bootcamp::Exercise < ApplicationRecord has_many :concepts, through: :exercise_concepts default_scope -> { order(:idx) } - scope :unlocked, -> { where('level_idx < ?', Bootcamp::Settings.level_idx) } + scope :unlocked, -> { where('level_idx <= ?', Bootcamp::Settings.level_idx) } def to_param = slug def locked? = level_idx > Bootcamp::Settings.level_idx diff --git a/app/models/bootcamp/project.rb b/app/models/bootcamp/project.rb index d767e4a559..1544b3b1eb 100644 --- a/app/models/bootcamp/project.rb +++ b/app/models/bootcamp/project.rb @@ -6,6 +6,10 @@ class Bootcamp::Project < ApplicationRecord # TODO: Fix the rubocop error has_many :exercises, class_name: "Bootcamp::Exercise" # rubocop:disable Rails/HasManyOrHasOneDependent + def self.unlocked + where(id: Bootcamp::Exercise.unlocked.select(:project_id)) + end + def to_param = slug def icon_url = "#{Exercism.config.website_icons_host}/bootcamp/projects/#{slug}.svg" diff --git a/app/views/bootcamp/concepts/show.html.haml b/app/views/bootcamp/concepts/show.html.haml index e8e50323e2..c924b41aa3 100644 --- a/app/views/bootcamp/concepts/show.html.haml +++ b/app/views/bootcamp/concepts/show.html.haml @@ -2,13 +2,13 @@ %header .c-breadcrumbs .lg-container.container - = link_to concepts_path do + = link_to bootcamp_concepts_path do = graphical_icon 'concepts' .span All Concepts .seperator / - @concept.parents.each do |parent| - = link_to parent.title, concept_path(parent) + = link_to parent.title, bootcamp_concept_path(parent) .seperator / .title= @concept.title diff --git a/app/views/bootcamp/dashboard/index.html.haml b/app/views/bootcamp/dashboard/index.html.haml index 88632d000a..fc060c92a1 100644 --- a/app/views/bootcamp/dashboard/index.html.haml +++ b/app/views/bootcamp/dashboard/index.html.haml @@ -1,60 +1,82 @@ #page-bootcamp-dashboard - if Bootcamp::Settings.level_idx.zero? - .lg-container - %div{ class: 'max-w-[720px] mx-auto' } - %h1 Welcome to the Exercism Bootcamp! - %p.large Thanks so much for joining the Bootcamp. We're really excited to share our passion for programming with you! - %h2 Introductory Session - %p - Our kick-off session is at 18:00 UTC on Saturday Jan 11th. - We'll introduce you to the Bootcamp, explain how it works, and answer any questions you have. - Then we'll dig into the first steps on your coding journey. - %p - %strong.font-semibold Check back here - to get a link shortly before the session starts. - - %h2 Forum & Discord - %p - We will be using Exercism's Forum and Discord server throughout this course to get unstuck and hang out. - Both have dedicated bootcamp areas which - %strong.font-medium you can access now. - %ul - %li - %strong.font-semibold The forum (#{link_to 'exercism.org/r/forum', 'https://exercism.org/r/forum', class: 'text-linkColor'}) - is the space to get help or ask questions. There is a dedicated Bootcamp category. Log in with your Exercism account and read - = link_to 'this post', 'https://forum.exercism.org/t/welcome-to-the-bootcamp-forum-space/14389', class: 'text-linkColor font-semibold' - to get started. - %li - %strong.font-semibold Our Discord Server (#{link_to 'exercism.org/r/discord', 'https://exercism.org/r/discord', class: 'text-linkColor'}) - is the place to go to chat and hang out with other participants. There is a dedicated #bootcamp channel reserved for you. - %strong.font-semibold Please use threads - on Discord to keep things ordered (don't be offended if you forget and we move your messages!). - To get access to the Bootcamp Channels, make sure your Exercism and Discord accounts #{link_to 'are connected here', 'https://exercism.org/settings/integrations', class: 'text-linkColor font-semibold'}. - - - %h2 Weekly Rhythm - %p We've designed each week to follow a similar pattern. - %ul - %li Monday at 18:00 UTC: Introduction to the week's content (Live Session). - %li Tuesday to Friday: Work on the week's exercises. - %li Saturday at 18:00 UTC: Deep dive into the week's exercises (Live Session). - %p The live sessions will be streamed on our YouTube channel and will be available to watch later if you can't make it. - - %h2 Am I signed up correctly? - %p.pb-20 Yes. If you're seeing this page, then you're signed up and will be able to access the content after the 11th. + %section.intro + .lg-container + %div{ class: 'max-w-[720px] mx-auto' } + %h1 Welcome to the Exercism Bootcamp! + %p.large Thanks so much for joining the Bootcamp. We're really excited to share our passion for programming with you! + %h2 Introductory Session + %p.text-18 + Our kick-off session is at 18:00 UTC on Saturday Jan 11th. + We'll introduce you to the Bootcamp, explain how it works, and answer any questions you have. + Then we'll dig into the first steps on your coding journey. + %p.text-18 + %strong.font-semibold Check back here + to get a link shortly before the session starts. + + %h2 Forum & Discord + %p.text-18 + We will be using Exercism's Forum and Discord server throughout this course to get unstuck and hang out. + Both have dedicated bootcamp areas which + %strong.font-medium you can access now. + %ul + %li + %strong.font-semibold The forum (#{link_to 'exercism.org/r/forum', 'https://exercism.org/r/forum', class: 'text-linkColor'}) + is the space to get help or ask questions. There is a dedicated Bootcamp category. Log in with your Exercism account and read + = link_to 'this post', 'https://forum.exercism.org/t/welcome-to-the-bootcamp-forum-space/14389', class: 'text-linkColor font-semibold' + to get started. + %li + %strong.font-semibold Our Discord Server (#{link_to 'exercism.org/r/discord', 'https://exercism.org/r/discord', class: 'text-linkColor'}) + is the place to go to chat and hang out with other participants. There is a dedicated #bootcamp channel reserved for you. + %strong.font-semibold Please use threads + on Discord to keep things ordered (don't be offended if you forget and we move your messages!). + To get access to the Bootcamp Channels, make sure your Exercism and Discord accounts #{link_to 'are connected here', 'https://exercism.org/settings/integrations', class: 'text-linkColor font-semibold'}. + + + %h2 Weekly Rhythm + %p.text-18 + We've designed each week to follow a similar pattern. + %ul.text-18 + %li Monday at 18:00 UTC: Introduction to the week's content (Live Session). + %li Tuesday to Friday: Work on the week's exercises. + %li Saturday at 18:00 UTC: Deep dive into the week's exercises (Live Session). + %p.text-18 + The live sessions will be streamed on our YouTube channel and will be available to watch later if you can't make it. + + %h2 Am I signed up correctly? + %p.text-18.pb-20 + Yes. If you're seeing this page, then you're signed up and will be able to access the content after the 11th. - else - .lg-container - .grid.grid-cols-2 - .lhs - - if @exercise - - if @solution - %h2 Continue Where You Left Off - %p You have an exercise in progress. - = render 'exercise', exercise: @exercise, solution: @solution + %section.normal + .lg-container + .flex + .lhs.mr-48{ class: 'w-full max-w-[800px]' } + - if @exercise + - if @solution + %h2 Continue Where You Left Off + %p.mb-8 You have an exercise in progress. + + = render ViewComponents::Bootcamp::ExerciseWidget.new(@exercise, solution: @solution) + - else + %h2 Start new exercise + %p You have a new exercise available to work on. + = render ViewComponents::Bootcamp::ExerciseWidget.new(@exercise) - else - %h2 Start new exercise - %p You have a new exercise available to work on. - = render 'exercise', exercise: @exercise - - else - You have no exercises available. + You have no exercises available. + + .rhs.ml-auto{ class: 'max-w-[400px]' } + %h2 Make the most of the Bootcamp + %p.mb-12 The more you take part in all areas of the Bootcamp, the more you'll learn. + .flex.flex-col.gap-16 + = link_to "https://exercism.org/r/discord", class: "border-1 border-borderColor5 block rounded-5 px-16 py-12 bg-white shadow-sm" do + = graphical_icon 'external-site-discord-blue', css_class: 'w-[48px] h-[48px] mb-12' + %h4.text-20.font-semibold.mb-6 Chat on Discord + %p Use the exclusive #bootcamp area on Exercism's Discord server to chat to other Bootcamp students and mentors. + + = link_to "https://forum.exercism.org/c/bootcamp/661", class: "border-1 border-borderColor5 block rounded-5 px-16 py-12 bg-white shadow-sm" do + = graphical_icon 'discourser', css_class: 'w-[48px] h-[48px] mb-12' + %h4.text-20.font-semibold.mb-6 Ask questions on the Forum + %p + Use the Bootcamp Category on the forum to ask questions and get unstuck. + Read the pinned post to learn more. diff --git a/app/views/bootcamp/projects/index.html.haml b/app/views/bootcamp/projects/index.html.haml index d922598285..66181bd05a 100644 --- a/app/views/bootcamp/projects/index.html.haml +++ b/app/views/bootcamp/projects/index.html.haml @@ -7,9 +7,12 @@ .lg-container .title-bar %h1.font-semibold.text-20.mb-8 Projects - %p More projects will unlock as you progress through the Bootcamp. + %p.text-18{ class: "max-w-[850px]" } + Throughout the bootcamp you'll build a variety of projects. Each contains multiple exercises that will unlock as the bootcamp progresses. + More projects will unlock as you progress. .lg-container .grid.grid-cols-3.gap-8 - - @user_projects.each do |user_project| - = render ViewComponents::Bootcamp::ProjectWidget.new(user_project.project, user_project:) + - Bootcamp::Project.unlocked.each do |project| + - user_project = @user_projects[project.id] + = render ViewComponents::Bootcamp::ProjectWidget.new(project, user_project:) diff --git a/app/views/bootcamp/projects/show.html.haml b/app/views/bootcamp/projects/show.html.haml index cd625c0392..e95a8e2f58 100644 --- a/app/views/bootcamp/projects/show.html.haml +++ b/app/views/bootcamp/projects/show.html.haml @@ -40,7 +40,6 @@ = button_to bootcamp_drawings_path, method: :post, class: "drawing new" do New Drawing - .rhs - num_completed = @project.exercises.to_a.count { |exercise| @solutions[exercise.id]&.completed? } - num_unlocked = @project.exercises.to_a.count(&:unlocked?) @@ -49,5 +48,5 @@ You have completed #{num_completed} of #{num_unlocked} unlockable exercises in this project. New exercises will unlock as the Bootcamp progresses. .exercises.flex.flex-col.gap-16 - - @project.exercises.each do |exercise| + - @project.exercises.unlocked.each do |exercise| = render ViewComponents::Bootcamp::ExerciseWidget.new(exercise, solution: @solutions[exercise.id], user_project: @user_project) diff --git a/app/views/layouts/bootcamp-ui.haml b/app/views/layouts/bootcamp-ui.haml index 0108761dac..270e2a3439 100644 --- a/app/views/layouts/bootcamp-ui.haml +++ b/app/views/layouts/bootcamp-ui.haml @@ -61,7 +61,7 @@ %body{ class: body_class } %header.c-site-header - .sm-container + %div{ class: Bootcamp::Settings.level_idx.zero? ? "sm-container" : "lg-container" } .container %img{ src: "https://assets.exercism.org/assets/bootcamp/exercism-face-light-2fc4ffad44f295d2e900ab2d2198d2280128dfcd.svg" } .content @@ -69,4 +69,5 @@ Bootcamp = link_to "Back to Exercism →", "https://exercism.org", class: 'ml-auto text-linkColor font-semibold text-16' + = yield diff --git a/bootcamp_content/projects/drawing/config.json b/bootcamp_content/projects/drawing/config.json index 1edd49ec65..b540545951 100644 --- a/bootcamp_content/projects/drawing/config.json +++ b/bootcamp_content/projects/drawing/config.json @@ -1,6 +1,6 @@ { "slug": "drawing", "title": "Drawing", - "description": "The world of drawing", - "exercises": ["jumbled-house", "loops"] + "description": "With only a few basic shapes you can draw and animate almost anything.", + "exercises": ["penguin", "jumbled-house", "loops"] } diff --git a/bootcamp_content/projects/drawing/exercises/jumbled-house/config.json b/bootcamp_content/projects/drawing/exercises/jumbled-house/config.json index 1495979985..4bfed78f5b 100644 --- a/bootcamp_content/projects/drawing/exercises/jumbled-house/config.json +++ b/bootcamp_content/projects/drawing/exercises/jumbled-house/config.json @@ -5,57 +5,25 @@ "level": 1, "concepts": [], "tests_type": "state", - "readonly_ranges": [ - { "from": 1, "to": 10 }, - { "from": 12, "to": 14 }, - { "from": 16, "to": 18 }, - { "from": 20, "to": 22 }, - { "from": 24, "to": 26 }, - { "from": 28, "to": 30 } - ], "tasks": [ { - "name": "Position the frame of house", + "name": "Correctly arrange the house", "tests": [ { - "slug": "position-frame", + "slug": "draw-the-house", "name": "Position the frame of the house.", "function": "main", "checks": [ { "name": "getRectangleAt(20,50,60,40)", "matcher": "toExist", - "error_html": "The frame of the house is not at (20,50,60,40)." - } - ] - } - ] - }, - { - "name": "Position the roof of house", - "tests": [ - { - "slug": "position-roof", - "name": "Position the roof of the house.", - "function": "main", - "checks": [ + "error_html": "The frame of the house is not correct." + }, { "name": "getTriangleAt(16,50, 50,30, 84,50)", "matcher": "toExist", "error_html": "The roof of the house is not at the correct position." - } - ] - } - ] - }, - { - "name": "Position the windows", - "tests": [ - { - "slug": "position-windows", - "name": "Position the windows of the house.", - "function": "main", - "checks": [ + }, { "name": "getRectangleAt(30,55,12,13)", "matcher": "toExist", @@ -65,19 +33,7 @@ "name": "getRectangleAt(58,55,12,13)", "matcher": "toExist", "error_html": "The right window frame isn't positioned correctly" - } - ] - } - ] - }, - { - "name": "Position the door", - "tests": [ - { - "slug": "position-door", - "name": "Position the door", - "function": "main", - "checks": [ + }, { "name": "getRectangleAt(43,72,14,18)", "matcher": "toExist", diff --git a/bootcamp_content/projects/drawing/exercises/jumbled-house/example.jiki b/bootcamp_content/projects/drawing/exercises/jumbled-house/example.jiki index 2c74a35e75..fcee7b813a 100644 --- a/bootcamp_content/projects/drawing/exercises/jumbled-house/example.jiki +++ b/bootcamp_content/projects/drawing/exercises/jumbled-house/example.jiki @@ -1,31 +1,31 @@ // The sky -change_fill_color("#add8e6") +fill_color_hex("#add8e6") rectangle(0,0,100,100) // The grass -change_fill_color("#3cb372") +fill_color_hex("#3cb372") rectangle(0,80,100,100) // The frame of the house -change_fill_color("#f0985b") +fill_color_hex("#f0985b") rectangle(20,50,60,40) // The roof -change_fill_color("#8b4513") +fill_color_hex("#8b4513") triangle(16,50, 50,30, 84,50) // The left window -change_fill_color("#FFFFFF") +fill_color_hex("#FFFFFF") rectangle(30,55,12,13) // The second window -change_fill_color("#FFFFFF") +fill_color_hex("#FFFFFF") rectangle(58,55,12,13) // The door -change_fill_color("#A0512D") +fill_color_hex("#A0512D") rectangle(43,72,14,18) // The door knob -change_fill_color("#FFDF00") +fill_color_hex("#FFDF00") circle(55,81,1) \ No newline at end of file diff --git a/bootcamp_content/projects/drawing/exercises/jumbled-house/introduction.md b/bootcamp_content/projects/drawing/exercises/jumbled-house/introduction.md index 4ccc1960e7..9295d4112d 100644 --- a/bootcamp_content/projects/drawing/exercises/jumbled-house/introduction.md +++ b/bootcamp_content/projects/drawing/exercises/jumbled-house/introduction.md @@ -1,6 +1,15 @@ # Jumbled House -- The frame of the house (the big square) should be 60 wide, 40 height, and have it's top-left corner at 20,50. -- The roof should overlap the house by 4 on each side, and have a height of 20. -- The windows have a width of 12 and a height of 13. They sit 10 from the edges, and 5 from the top. -- The door is 14 wide and 18 tall, and sits at the bottom of the house in the center. The little door knob has a radius of 1, is inset 1 from the right, and is vertically centered in the door. +Your task is to rearrange the pieces of the house. They are both misplaced and the wrong sizes. Follow the instructions carefully to piece everything together! + +It should look like this when it's finished: + + + +Try not to guess at this exercise. Go slowly and precisely. You should be able to work everything out before even pressing Check Scenarios once (although you'll probably need a couple of tries to get it right). + +- The frame of the house (the big square) should be 60 wide and 40 height. It should have it's top-left corner at 20x50. +- The roof sits snuggly on top of the house's frame. It should overlap the left and right of the house by 4 on each side. It should have a height of 20, and it's point should be centered horizontally. +- The windows are both the same size, with have a width of 12 and a height of 13. They both sit 5 from the top of the house frame, and 10 inset from the sides. +- The door is 14 wide and 18 tall, and sits at the bottom of the house in the center. +- The little door knob has a radius of 1, is inset 1 from the right, and is vertically centered in the door. diff --git a/bootcamp_content/projects/drawing/exercises/jumbled-house/stub.jiki b/bootcamp_content/projects/drawing/exercises/jumbled-house/stub.jiki index ab5ade5bec..4e8a455a98 100644 --- a/bootcamp_content/projects/drawing/exercises/jumbled-house/stub.jiki +++ b/bootcamp_content/projects/drawing/exercises/jumbled-house/stub.jiki @@ -1,31 +1,31 @@ // The sky -change_fill_color("#add8e6") +fill_color_hex("#add8e6") rectangle(0,0,100,100) // The grass -change_fill_color("#3cb372") +fill_color_hex("#3cb372") rectangle(0,80,100,100) // The frame of the house -change_fill_color("#F08080") -rectangle(10,20,60,40) +fill_color_hex("#f0985b") +rectangle(10,20,55,30) // The roof -change_fill_color("#8b4513") -triangle(26,90, 60,70, 94,90) +fill_color_hex("#8b4513") +triangle(26,90, 60,60, 100,90) // The left window -change_fill_color("#FFFFFF") -rectangle(10,15,12,13) +fill_color_hex("#FFFFFF") +rectangle(10,15,6,7) // The second window -change_fill_color("#FFFFFF") -rectangle(18,85,12,13) +fill_color_hex("#FFFFFF") +rectangle(18,55,22,23) // The door -change_fill_color("#A0512D") -rectangle(83,12,14,18) +fill_color_hex("#A0512D") +rectangle(83,12,10,16) // The door knob -change_fill_color("#FFDF00") -circle(95,21,1) \ No newline at end of file +fill_color_hex("#FFDF00") +circle(91,20,1) \ No newline at end of file diff --git a/bootcamp_content/projects/drawing/exercises/jumbled-house/task-1.md b/bootcamp_content/projects/drawing/exercises/jumbled-house/task-1.md index 9037ae965a..982df7afec 100644 --- a/bootcamp_content/projects/drawing/exercises/jumbled-house/task-1.md +++ b/bootcamp_content/projects/drawing/exercises/jumbled-house/task-1.md @@ -1 +1 @@ -Move the frame of the house, so that it's bottom left coordinate is at (20,40). It's size is already set correctly at a width of 60 and a height of 50. +Draw the house following the rules above. Remember to focus on precision for this exercise. Take your time and have fun! diff --git a/bootcamp_content/projects/drawing/exercises/penguin/config.json b/bootcamp_content/projects/drawing/exercises/penguin/config.json new file mode 100644 index 0000000000..f759cf736a --- /dev/null +++ b/bootcamp_content/projects/drawing/exercises/penguin/config.json @@ -0,0 +1,109 @@ +{ + "title": "Penguin", + "description": "Make the penguin symmetrical", + "project_type": "draw", + "level": 1, + "concepts": [], + "tests_type": "state", + "readonly_ranges": [], + "tasks": [ + { + "name": "Draw the scene", + "tests": [ + { + "slug": "draw-scence", + "name": "Make the penguin symmetrical.", + "description_html": "Fix all the TODO comments to make the penguin symmetrical.", + "function": "main", + "checks": [ + { + "name": "getRectangleAt(0, 0, 100, 100)", + "matcher": "toExist", + "error_html": "The sky has gone wrong." + }, + { + "name": "getRectangleAt(0, 70, 100, 30)", + "matcher": "toExist", + "error_html": "The ground has gone wrong." + }, + { + "name": "getEllipseAt(28, 55, 10, 25)", + "matcher": "toExist", + "error_html": "The left wing doesn't seem right." + }, + { + "name": "getEllipseAt(72, 55, 10, 25)", + "matcher": "toExist", + "error_html": "The right wing doesn't seem right." + }, + { + "name": "getEllipseAt(50, 53, 25, 40)", + "matcher": "toExist", + "error_html": "The outer body has gone wrong." + }, + { + "name": "getEllipseAt(50, 50, 21, 39)", + "matcher": "toExist", + "error_html": "The inner body has gone wrong." + }, + { + "name": "getCircleAt(50, 31, 23)", + "matcher": "toExist", + "error_html": "The head has gone wrong." + }, + { + "name": "getEllipseAt(41, 32, 11, 14)", + "matcher": "toExist", + "error_html": "The left side of the face doesn't look right." + }, + { + "name": "getEllipseAt(59, 32, 11, 14)", + "matcher": "toExist", + "error_html": "The right side of the face doesn't look right." + }, + { + "name": "getEllipseAt(50, 40, 16, 11)", + "matcher": "toExist", + "error_html": "The lower part of the face doesn't look right." + }, + { + "name": "getCircleAt(42, 33, 3)", + "matcher": "toExist", + "error_html": "The left eye seems off." + }, + { + "name": "getCircleAt(43, 34, 1)", + "matcher": "toExist", + "error_html": "The left iris seems off." + }, + { + "name": "getCircleAt(58, 33, 3)", + "matcher": "toExist", + "error_html": "The right eye seems off." + }, + { + "name": "getCircleAt(57, 34, 1)", + "matcher": "toExist", + "error_html": "The right iris seems off." + }, + { + "name": "getEllipseAt(40, 93, 7, 4)", + "matcher": "toExist", + "error_html": "The left foot's gone astray." + }, + { + "name": "getEllipseAt(60, 93, 7, 4)", + "matcher": "toExist", + "error_html": "The right foot's not right." + }, + { + "name": "getTriangleAt(46, 38, 54, 38, 50, 47)", + "matcher": "toExist", + "error_html": "The nose isn't right." + } + ] + } + ] + } + ] +} diff --git a/bootcamp_content/projects/drawing/exercises/penguin/example.jiki b/bootcamp_content/projects/drawing/exercises/penguin/example.jiki new file mode 100644 index 0000000000..9d57b7b4bd --- /dev/null +++ b/bootcamp_content/projects/drawing/exercises/penguin/example.jiki @@ -0,0 +1,43 @@ +// Light blue background +fill_color_hex("#ADD8E6") +rectangle(0, 0, 100, 100) + +// Ground +fill_color_hex("#ffffff") // Ice ground +rectangle(0, 70, 100, 30) // Ice ground + +// Penguin wings +fill_color_hex("#000000") // Black +ellipse(28, 55, 10, 25) // Left wing +ellipse(72, 55, 10, 25) // Right wing + +// Penguin body +fill_color_hex("#000000") // Black for the body +ellipse(50, 53, 25, 40) // Outer body (oval shape) +fill_color_hex("#ffffff") // White for the belly +ellipse(50, 50, 21, 39) // Inner belly (oval shape) + +// Penguin head +fill_color_hex("#000000") // Black +circle(50, 31, 23) // Head (circle) +fill_color_hex("#ffffff") // White for the face +ellipse(41, 32, 11, 14) // Left part of the face +ellipse(59, 32, 11, 14) // Right part of the face +ellipse(50, 40, 16, 11) // Lower part of the face + +// Penguin eyes +fill_color_hex("#000000") // Black +circle(42, 33, 3) // Left eye +fill_color_hex("#ffffff") // White +circle(43, 34, 1) // Left iris + +fill_color_hex("#000000") // Black +circle(58, 33, 3) // Right eye +fill_color_hex("#ffffff") // White +circle(57, 34, 1) // Right iris + +// Feet +fill_color_hex("#FFA500") +ellipse(40, 93, 7, 4) +ellipse(60, 93, 7, 4) +triangle(46, 38, 54, 38, 50, 47) \ No newline at end of file diff --git a/bootcamp_content/projects/drawing/exercises/penguin/introduction.md b/bootcamp_content/projects/drawing/exercises/penguin/introduction.md new file mode 100644 index 0000000000..28e0165746 --- /dev/null +++ b/bootcamp_content/projects/drawing/exercises/penguin/introduction.md @@ -0,0 +1,21 @@ +# The Penguin + +Your task is to make the penguin symmetrical. It should look like this: + + + +We've drawn the left hand side for you, and added `TODO` comments for each of the things you need to do. + +You'll need to think about setting the right colors before drawing things. + +For the nose, you should **change** the middle coordinates of the triangle. Don't add a new triangle. + +The functions used in this exercise are: + +- `circle(x, y, radius)` +- `rectangle(x, y, height, width)` +- `ellipse(x, y, radius_x, radius_y)` +- `triangle(x1,y1, x2,y2, x3,y3)` +- `fill_color_hex(hex)` + +If you need help remembering how to use any of these functions, you can watch back the video from week 1. diff --git a/bootcamp_content/projects/drawing/exercises/penguin/stub.jiki b/bootcamp_content/projects/drawing/exercises/penguin/stub.jiki new file mode 100644 index 0000000000..aec40e98c7 --- /dev/null +++ b/bootcamp_content/projects/drawing/exercises/penguin/stub.jiki @@ -0,0 +1,58 @@ +// Sky +fill_color_hex("#ADD8E6") +rectangle(0, 0, 100, 100) + +// Ground +fill_color_hex("#ffffff") +rectangle(0, 70, 100, 30) + +// Left Wing +fill_color_hex("#000000") +ellipse(28, 55, 10, 25) + +// +// TODO: Add the Right wing +// + +// Body +fill_color_hex("#000000") +ellipse(50, 53, 25, 40) +fill_color_hex("#ffffff") +ellipse(50, 50, 21, 39) + +// Head +fill_color_hex("#000000") +circle(50, 31, 23) + +// Left side of face +fill_color_hex("#ffffff") +ellipse(41, 32, 11, 14) + +// +// TODO: Add the right part of the face +// + +// Lower part of face +ellipse(50, 40, 16, 11) // Lower part of the face + +// Left eye +fill_color_hex("#000000") +circle(42, 33, 3) +fill_color_hex("#ffffff") +circle(43, 34, 1) + +// +// TODO: Add the right eye +// + +// Nose +fill_color_hex("#FFA500") +triangle(46, 38, 50, 38, 50, 47) // TODO: Change the nose to be symmetrical. + +// Left Foot +fill_color_hex("#FFA500") +ellipse(40, 93, 7, 4) + +// +// TODO: Add the right foot +// diff --git a/bootcamp_content/projects/drawing/exercises/penguin/task-1.md b/bootcamp_content/projects/drawing/exercises/penguin/task-1.md new file mode 100644 index 0000000000..c9fc02faac --- /dev/null +++ b/bootcamp_content/projects/drawing/exercises/penguin/task-1.md @@ -0,0 +1,5 @@ +Follow the `TODO` comments to make the penguin symmetrical. + +As you get each bit right, tidy up the comments by removing the `TODO` and replacing it with a nicer comment. + +Use the "Check Scenarios" button regularly, and have fun! diff --git a/bootcamp_content/projects/maze/config.json b/bootcamp_content/projects/maze/config.json index 28339524da..21dacb8aa3 100644 --- a/bootcamp_content/projects/maze/config.json +++ b/bootcamp_content/projects/maze/config.json @@ -1,6 +1,6 @@ { "slug": "maze", "title": "Maze", - "description": "Use then implement a basic maze", + "description": "Solve a maze, automate it, then build your own maze generator.", "exercises": ["manual-solve", "automated-solve"] } diff --git a/bootcamp_content/projects/weather/config.json b/bootcamp_content/projects/weather/config.json new file mode 100644 index 0000000000..a3e1c5cfac --- /dev/null +++ b/bootcamp_content/projects/weather/config.json @@ -0,0 +1,6 @@ +{ + "slug": "weather", + "title": "Weather Forecast", + "description": "Build a beautiful, functional, weather forecast app", + "exercises": ["cloud-rain-sun"] +} diff --git a/bootcamp_content/projects/weather/exercises/cloud-rain-sun/config.json b/bootcamp_content/projects/weather/exercises/cloud-rain-sun/config.json new file mode 100644 index 0000000000..589fc056a4 --- /dev/null +++ b/bootcamp_content/projects/weather/exercises/cloud-rain-sun/config.json @@ -0,0 +1,91 @@ +{ + "title": "Clouds, Rain, and Sun", + "description": "Make a compound image for clouds, rain and sun", + "project_type": "draw", + "level": 1, + "concepts": [], + "tests_type": "state", + "readonly_ranges": [], + "tasks": [ + { + "name": "Draw the scene", + "tests": [ + { + "slug": "draw-scene", + "name": "Draw the scene.", + "function": "main", + "setup_functions": [ + [ + "setBackgroundImage", + [ + "https://assets.exercism.org/bootcamp/graphics/cloud-rain-sun-template.png" + ] + ] + ], + "checks": [ + { + "name": "getCircleAt(75, 30, 15)", + "matcher": "toExist", + "error_html": "The sun isn't correct." + }, + { + "name": "getRectangleAt(25, 50, 50, undefined)", + "matcher": "toExist", + "error_html": "The base of the cloud isn't correct." + }, + { + "name": "getCircleAt(25, 50, 10)", + "matcher": "toExist", + "error_html": "The fluffy bits of the cloud aren't correct." + }, + { + "name": "getCircleAt(40, 40, 15)", + "matcher": "toExist", + "error_html": "The fluffy bits of the cloud aren't correct." + }, + { + "name": "getCircleAt(55, 40, 20)", + "matcher": "toExist", + "error_html": "The fluffy bits of the cloud aren't correct." + }, + { + "name": "getCircleAt(75, 50, 10)", + "matcher": "toExist", + "error_html": "The fluffy bits of the cloud aren't correct." + }, + { + "name": "getCircleAt(75, 50, 10)", + "matcher": "toExist", + "error_html": "The fluffy bits of the cloud aren't correct." + }, + { + "name": "getEllipseAt(30, 70, 3, 5)", + "matcher": "toExist", + "error_html": "The first rain drop isn't correct" + }, + { + "name": "getEllipseAt(50, 70, 3, 5)", + "matcher": "toExist", + "error_html": "The second rain drop isn't correct" + }, + { + "name": "getEllipseAt(70, 70, 3, 5)", + "matcher": "toExist", + "error_html": "The third rain drop isn't correct" + }, + { + "name": "getEllipseAt(40, 80, 3, 5)", + "matcher": "toExist", + "error_html": "The fourth rain drop isn't correct" + }, + { + "name": "getEllipseAt(60, 80, 3, 5)", + "matcher": "toExist", + "error_html": "The fifth rain drop isn't correct" + } + ] + } + ] + } + ] +} diff --git a/bootcamp_content/projects/weather/exercises/cloud-rain-sun/example.jiki b/bootcamp_content/projects/weather/exercises/cloud-rain-sun/example.jiki new file mode 100644 index 0000000000..d0029c7876 --- /dev/null +++ b/bootcamp_content/projects/weather/exercises/cloud-rain-sun/example.jiki @@ -0,0 +1,22 @@ +// Light blue background +fill_color_hex("#ADD8E6") +rectangle(0, 0, 100, 100) + +// Sun +fill_color_hex("#ffed06") +circle(75, 30, 15) + +// Cloud +fill_color_hex("#FFFFFF") +rectangle(25, 50, 50, 10) +circle(25, 50, 10) +circle(40, 40, 15) +circle(55, 40, 20) +circle(75, 50, 10) + +fill_color_hex("#56AEFF") +ellipse(30, 70, 3, 5) +ellipse(50, 70, 3, 5) +ellipse(70, 70, 3, 5) +ellipse(40, 80, 3, 5) +ellipse(60, 80, 3, 5) \ No newline at end of file diff --git a/bootcamp_content/projects/weather/exercises/cloud-rain-sun/introduction.md b/bootcamp_content/projects/weather/exercises/cloud-rain-sun/introduction.md new file mode 100644 index 0000000000..cff49f3907 --- /dev/null +++ b/bootcamp_content/projects/weather/exercises/cloud-rain-sun/introduction.md @@ -0,0 +1,25 @@ +# Cloud, Rain and Sun + +Your task is to draw a weather icon for a rainy day with the sun sneaking out behind the clouds. It should look something like this: + + + +We've drawn a template for you. +Your shapes should sit just inside the lines. + +To make things a little easier, nearly every value is divisible by 5 (e.g. the numbers you'll use are 5, 10, 15, etc). The only exception is the width of the raindrops. + +You'll need to use the following functions to draw things: + +- `circle(x, y, radius)` +- `rectangle(x, y, height, width)` +- `ellipse(x, y, radius_x, radius_y)` + +You can use whatever colors your like for the various components, and you can change color using either of the `fill_color` functions to change color: + +- `fill_color_hex(hex)` +- `fill_color_rgb(red, green, blue)` + +Remember to set the color before you draw using it. + +If you need help remembering how to use any of these functions, you can watch back the video from week 1. diff --git a/bootcamp_content/projects/weather/exercises/cloud-rain-sun/stub.jiki b/bootcamp_content/projects/weather/exercises/cloud-rain-sun/stub.jiki new file mode 100644 index 0000000000..2d9dc0d6d9 --- /dev/null +++ b/bootcamp_content/projects/weather/exercises/cloud-rain-sun/stub.jiki @@ -0,0 +1,5 @@ +// Sun + +// Cloud + +// Rain Drops \ No newline at end of file diff --git a/bootcamp_content/projects/weather/exercises/cloud-rain-sun/task-1.md b/bootcamp_content/projects/weather/exercises/cloud-rain-sun/task-1.md new file mode 100644 index 0000000000..32ce5f504f --- /dev/null +++ b/bootcamp_content/projects/weather/exercises/cloud-rain-sun/task-1.md @@ -0,0 +1,3 @@ +The aim of this exercise is for you to carefully think through what needs to be done and take one step at a time. Start by thinking what components need to go where, then position each one in turn using the template. You'll need to keep trying and tweaking things to line everything up. + +Take your time and have fun! diff --git a/bootcamp_content/projects/drawing/exercises/jumbled-house/task-2.md b/bootcamp_content/projects/weather/exercises/cloud-rain-sun/task-2.md similarity index 100% rename from bootcamp_content/projects/drawing/exercises/jumbled-house/task-2.md rename to bootcamp_content/projects/weather/exercises/cloud-rain-sun/task-2.md diff --git a/bootcamp_content/projects/drawing/exercises/jumbled-house/task-3.md b/bootcamp_content/projects/weather/exercises/cloud-rain-sun/task-3.md similarity index 100% rename from bootcamp_content/projects/drawing/exercises/jumbled-house/task-3.md rename to bootcamp_content/projects/weather/exercises/cloud-rain-sun/task-3.md diff --git a/bootcamp_content/projects/drawing/exercises/jumbled-house/task-4.md b/bootcamp_content/projects/weather/exercises/cloud-rain-sun/task-4.md similarity index 100% rename from bootcamp_content/projects/drawing/exercises/jumbled-house/task-4.md rename to bootcamp_content/projects/weather/exercises/cloud-rain-sun/task-4.md diff --git a/bootcamp_content/projects/weather/introduction.md b/bootcamp_content/projects/weather/introduction.md new file mode 100644 index 0000000000..e320953428 --- /dev/null +++ b/bootcamp_content/projects/weather/introduction.md @@ -0,0 +1,5 @@ +# Two Fer + +## Overview + +With a little bit of code, you can create almost any drawing. diff --git a/bootcamp_content/projects/weather/tree.jiki b/bootcamp_content/projects/weather/tree.jiki new file mode 100644 index 0000000000..342ca5fe90 --- /dev/null +++ b/bootcamp_content/projects/weather/tree.jiki @@ -0,0 +1,14 @@ + +change_fill_color("#aa95aa") +rect(13,28,13,50) +change_fill_color("#33ff33") +change_pen_color("#33ff33") +circle(4,5,8) +circle(17,5,8) +circle(10,10,10) +circle(19,19,8) +circle(4,16,8) + +change_pen_color("#885588") +change_fill_color("#885588") +circle(15.5,40,3.8) \ No newline at end of file diff --git a/db/bootcamp_seeds.rb b/db/bootcamp_seeds.rb index 5c803e420e..65fb87a884 100644 --- a/db/bootcamp_seeds.rb +++ b/db/bootcamp_seeds.rb @@ -1,6 +1,7 @@ return unless Rails.env.development? # rubocop:disable Layout/LineLength +Bootcamp::UserProject.destroy_all Bootcamp::Submission.destroy_all Bootcamp::Solution.destroy_all Bootcamp::Exercise.destroy_all @@ -52,6 +53,7 @@ def exercise_config_for(project_slug, drawing maze wordle + weather ] projects.each do |project_slug| diff --git a/test/javascript/interpreter/languages/javascript/interpreter.test.ts b/test/javascript/interpreter/languages/javascript/interpreter.test.ts index 9dd9ad278b..e17d32123f 100644 --- a/test/javascript/interpreter/languages/javascript/interpreter.test.ts +++ b/test/javascript/interpreter/languages/javascript/interpreter.test.ts @@ -1296,7 +1296,7 @@ describe('errors', () => { expect(frames[0].code).toBe('echo(1, 2)') expect(frames[0].error).not.toBeNull() expect(frames[0].error!.category).toBe('RuntimeError') - expect(frames[0].error!.type).toBe('InvalidNumberOfArguments') + expect(frames[0].error!.type).toBe('TooManyArguments') expect(error).toBeNull() }) @@ -1317,7 +1317,7 @@ describe('errors', () => { expect(frames[0].code).toBe('echo()') expect(frames[0].error).not.toBeNull() expect(frames[0].error!.category).toBe('RuntimeError') - expect(frames[0].error!.type).toBe('InvalidNumberOfArguments') + expect(frames[0].error!.type).toBe('TooFewArguments') expect(error).toBeNull() }) diff --git a/test/javascript/interpreter/languages/jikiscript/interpreter.test.ts b/test/javascript/interpreter/languages/jikiscript/interpreter.test.ts index 96ec44b4e8..7b9464293a 100644 --- a/test/javascript/interpreter/languages/jikiscript/interpreter.test.ts +++ b/test/javascript/interpreter/languages/jikiscript/interpreter.test.ts @@ -1172,14 +1172,18 @@ describe('errors', () => { }, ], } - const { frames, error } = interpret('echo(1, 2)', context) + const { frames, error } = interpret('echo(1)', context) + expect(frames).toBeArrayOfSize(1) expect(frames[0].line).toBe(1) expect(frames[0].status).toBe('ERROR') - expect(frames[0].code).toBe('echo(1, 2)') + expect(frames[0].code).toBe('echo(1)') expect(frames[0].error).not.toBeNull() expect(frames[0].error!.category).toBe('RuntimeError') - expect(frames[0].error!.type).toBe('InvalidNumberOfArguments') + expect(frames[0].error!.type).toBe('TooManyArguments') + expect(frames[0].error!.message).toBe( + 'Did you add an extra argument? This function expects to be called with 0 arguments but you called it with 1.' + ) expect(error).toBeNull() }) @@ -1200,7 +1204,10 @@ describe('errors', () => { expect(frames[0].code).toBe('echo()') expect(frames[0].error).not.toBeNull() expect(frames[0].error!.category).toBe('RuntimeError') - expect(frames[0].error!.type).toBe('InvalidNumberOfArguments') + expect(frames[0].error!.type).toBe('TooFewArguments') + expect(frames[0].error!.message).toBe( + 'Did you forget an argument? This function expects to be called with 1 arguments but you called it with 0.' + ) expect(error).toBeNull() }) })